Merge branch 'main' into aeisenberg/remote-query-save
This commit is contained in:
@@ -3,6 +3,8 @@
|
|||||||
## [UNRELEASED]
|
## [UNRELEASED]
|
||||||
|
|
||||||
- Fix a bug where database upgrades could not be resolved if some of the target pack's dependencies are outside of the workspace. [#1138](https://github.com/github/vscode-codeql/pull/1138)
|
- Fix a bug where database upgrades could not be resolved if some of the target pack's dependencies are outside of the workspace. [#1138](https://github.com/github/vscode-codeql/pull/1138)
|
||||||
|
- Open the query server logs for query errors (instead of the extension log). This will make it easier to track down query errors. [#1158](https://github.com/github/vscode-codeql/pull/1158)
|
||||||
|
- Fix a bug where queries took a long time to run if there are no folders in the workspace. [#1157](https://github.com/github/vscode-codeql/pull/1157)
|
||||||
|
|
||||||
## 1.5.11 - 10 February 2022
|
## 1.5.11 - 10 February 2022
|
||||||
|
|
||||||
|
|||||||
@@ -514,8 +514,7 @@ export class CodeQLCliServer implements Disposable {
|
|||||||
async resolveLibraryPath(workspaces: string[], queryPath: string): Promise<QuerySetup> {
|
async resolveLibraryPath(workspaces: string[], queryPath: string): Promise<QuerySetup> {
|
||||||
const subcommandArgs = [
|
const subcommandArgs = [
|
||||||
'--query', queryPath,
|
'--query', queryPath,
|
||||||
'--additional-packs',
|
...this.getAdditionalPacksArg(workspaces)
|
||||||
workspaces.join(path.delimiter)
|
|
||||||
];
|
];
|
||||||
return await this.runJsonCodeQlCliCommand<QuerySetup>(['resolve', 'library-path'], subcommandArgs, 'Resolving library paths');
|
return await this.runJsonCodeQlCliCommand<QuerySetup>(['resolve', 'library-path'], subcommandArgs, 'Resolving library paths');
|
||||||
}
|
}
|
||||||
@@ -528,8 +527,7 @@ export class CodeQLCliServer implements Disposable {
|
|||||||
const subcommandArgs = [
|
const subcommandArgs = [
|
||||||
'--format', 'bylanguage',
|
'--format', 'bylanguage',
|
||||||
queryUri.fsPath,
|
queryUri.fsPath,
|
||||||
'--additional-packs',
|
...this.getAdditionalPacksArg(workspaces)
|
||||||
workspaces.join(path.delimiter)
|
|
||||||
];
|
];
|
||||||
return JSON.parse(await this.runCodeQlCliCommand(['resolve', 'queries'], subcommandArgs, 'Resolving query by language'));
|
return JSON.parse(await this.runCodeQlCliCommand(['resolve', 'queries'], subcommandArgs, 'Resolving query by language'));
|
||||||
}
|
}
|
||||||
@@ -562,6 +560,17 @@ export class CodeQLCliServer implements Disposable {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Issues an internal clear-cache command to the cli server. This
|
||||||
|
* command is used to clear the qlpack cache of the server.
|
||||||
|
*
|
||||||
|
* This cache is generally cleared every 1s. This method is used
|
||||||
|
* to force an early clearing of the cache.
|
||||||
|
*/
|
||||||
|
public async clearCache(): Promise<void> {
|
||||||
|
await this.runCodeQlCliCommand(['clear-cache'], [], 'Clearing qlpack cache');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs QL tests.
|
* Runs QL tests.
|
||||||
* @param testPaths Full paths of the tests to run.
|
* @param testPaths Full paths of the tests to run.
|
||||||
@@ -573,7 +582,7 @@ export class CodeQLCliServer implements Disposable {
|
|||||||
): AsyncGenerator<TestCompleted, void, unknown> {
|
): AsyncGenerator<TestCompleted, void, unknown> {
|
||||||
|
|
||||||
const subcommandArgs = this.cliConfig.additionalTestArguments.concat([
|
const subcommandArgs = this.cliConfig.additionalTestArguments.concat([
|
||||||
'--additional-packs', workspaces.join(path.delimiter),
|
...this.getAdditionalPacksArg(workspaces),
|
||||||
'--threads',
|
'--threads',
|
||||||
this.cliConfig.numberTestThreads.toString(),
|
this.cliConfig.numberTestThreads.toString(),
|
||||||
...testPaths
|
...testPaths
|
||||||
@@ -595,8 +604,12 @@ export class CodeQLCliServer implements Disposable {
|
|||||||
|
|
||||||
/** Resolves the ML models that should be available when evaluating a query. */
|
/** Resolves the ML models that should be available when evaluating a query. */
|
||||||
async resolveMlModels(additionalPacks: string[]): Promise<MlModelsInfo> {
|
async resolveMlModels(additionalPacks: string[]): Promise<MlModelsInfo> {
|
||||||
return await this.runJsonCodeQlCliCommand<MlModelsInfo>(['resolve', 'ml-models'], ['--additional-packs',
|
return await this.runJsonCodeQlCliCommand<MlModelsInfo>(
|
||||||
additionalPacks.join(path.delimiter)], 'Resolving ML models', false);
|
['resolve', 'ml-models'],
|
||||||
|
this.getAdditionalPacksArg(additionalPacks),
|
||||||
|
'Resolving ML models',
|
||||||
|
false
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -761,7 +774,7 @@ export class CodeQLCliServer implements Disposable {
|
|||||||
* @returns A list of database upgrade script directories
|
* @returns A list of database upgrade script directories
|
||||||
*/
|
*/
|
||||||
async resolveUpgrades(dbScheme: string, searchPath: string[], allowDowngradesIfPossible: boolean, targetDbScheme?: string): Promise<UpgradesInfo> {
|
async resolveUpgrades(dbScheme: string, searchPath: string[], allowDowngradesIfPossible: boolean, targetDbScheme?: string): Promise<UpgradesInfo> {
|
||||||
const args = ['--additional-packs', searchPath.join(path.delimiter), '--dbscheme', dbScheme];
|
const args = [...this.getAdditionalPacksArg(searchPath), '--dbscheme', dbScheme];
|
||||||
if (targetDbScheme) {
|
if (targetDbScheme) {
|
||||||
args.push('--target-dbscheme', targetDbScheme);
|
args.push('--target-dbscheme', targetDbScheme);
|
||||||
if (allowDowngradesIfPossible && await this.cliConstraints.supportsDowngrades()) {
|
if (allowDowngradesIfPossible && await this.cliConstraints.supportsDowngrades()) {
|
||||||
@@ -783,7 +796,7 @@ export class CodeQLCliServer implements Disposable {
|
|||||||
* @returns A dictionary mapping qlpack name to the directory it comes from
|
* @returns A dictionary mapping qlpack name to the directory it comes from
|
||||||
*/
|
*/
|
||||||
resolveQlpacks(additionalPacks: string[], searchPath?: string[]): Promise<QlpacksInfo> {
|
resolveQlpacks(additionalPacks: string[], searchPath?: string[]): Promise<QlpacksInfo> {
|
||||||
const args = ['--additional-packs', additionalPacks.join(path.delimiter)];
|
const args = this.getAdditionalPacksArg(additionalPacks);
|
||||||
if (searchPath?.length) {
|
if (searchPath?.length) {
|
||||||
args.push('--search-path', path.join(...searchPath));
|
args.push('--search-path', path.join(...searchPath));
|
||||||
}
|
}
|
||||||
@@ -829,7 +842,7 @@ export class CodeQLCliServer implements Disposable {
|
|||||||
* @returns A list of query files found.
|
* @returns A list of query files found.
|
||||||
*/
|
*/
|
||||||
async 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)];
|
const args = this.getAdditionalPacksArg(additionalPacks);
|
||||||
if (searchPath !== undefined) {
|
if (searchPath !== undefined) {
|
||||||
args.push('--search-path', path.join(...searchPath));
|
args.push('--search-path', path.join(...searchPath));
|
||||||
}
|
}
|
||||||
@@ -862,8 +875,7 @@ export class CodeQLCliServer implements Disposable {
|
|||||||
'-o',
|
'-o',
|
||||||
outputPath,
|
outputPath,
|
||||||
dir,
|
dir,
|
||||||
'--additional-packs',
|
...this.getAdditionalPacksArg(workspaceFolders)
|
||||||
workspaceFolders.join(path.delimiter)
|
|
||||||
];
|
];
|
||||||
if (!precompile && await this.cliConstraints.supportsNoPrecompile()) {
|
if (!precompile && await this.cliConstraints.supportsNoPrecompile()) {
|
||||||
args.push('--no-precompile');
|
args.push('--no-precompile');
|
||||||
@@ -918,6 +930,12 @@ export class CodeQLCliServer implements Disposable {
|
|||||||
throw new Error('No distribution found');
|
throw new Error('No distribution found');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getAdditionalPacksArg(paths: string[]): string[] {
|
||||||
|
return paths.length
|
||||||
|
? ['--additional-packs', paths.join(path.delimiter)]
|
||||||
|
: [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -160,7 +160,8 @@ export function commandRunner(
|
|||||||
export function commandRunnerWithProgress<R>(
|
export function commandRunnerWithProgress<R>(
|
||||||
commandId: string,
|
commandId: string,
|
||||||
task: ProgressTask<R>,
|
task: ProgressTask<R>,
|
||||||
progressOptions: Partial<ProgressOptions>
|
progressOptions: Partial<ProgressOptions>,
|
||||||
|
outputLogger = logger
|
||||||
): Disposable {
|
): Disposable {
|
||||||
return commands.registerCommand(commandId, async (...args: any[]) => {
|
return commands.registerCommand(commandId, async (...args: any[]) => {
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
@@ -177,9 +178,9 @@ export function commandRunnerWithProgress<R>(
|
|||||||
if (e instanceof UserCancellationException) {
|
if (e instanceof UserCancellationException) {
|
||||||
// User has cancelled this action manually
|
// User has cancelled this action manually
|
||||||
if (e.silent) {
|
if (e.silent) {
|
||||||
void logger.log(errorMessage);
|
void outputLogger.log(errorMessage);
|
||||||
} else {
|
} else {
|
||||||
void showAndLogWarningMessage(errorMessage);
|
void showAndLogWarningMessage(errorMessage, { outputLogger });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Include the full stack in the error log only.
|
// Include the full stack in the error log only.
|
||||||
@@ -187,6 +188,7 @@ export function commandRunnerWithProgress<R>(
|
|||||||
? `${errorMessage}\n${e.stack}`
|
? `${errorMessage}\n${e.stack}`
|
||||||
: errorMessage;
|
: errorMessage;
|
||||||
void showAndLogErrorMessage(errorMessage, {
|
void showAndLogErrorMessage(errorMessage, {
|
||||||
|
outputLogger,
|
||||||
fullMessage
|
fullMessage
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,8 @@ import {
|
|||||||
showAndLogErrorMessage,
|
showAndLogErrorMessage,
|
||||||
showAndLogWarningMessage,
|
showAndLogWarningMessage,
|
||||||
showAndLogInformationMessage,
|
showAndLogInformationMessage,
|
||||||
showInformationMessageWithAction
|
showInformationMessageWithAction,
|
||||||
|
tmpDir
|
||||||
} from './helpers';
|
} from './helpers';
|
||||||
import { assertNever } from './pure/helpers-pure';
|
import { assertNever } from './pure/helpers-pure';
|
||||||
import { spawnIdeServer } from './ide-server';
|
import { spawnIdeServer } from './ide-server';
|
||||||
@@ -538,6 +539,7 @@ async function activateWithInstalledDistribution(
|
|||||||
// Note we must update the query history view after showing results as the
|
// Note we must update the query history view after showing results as the
|
||||||
// display and sorting might depend on the number of results
|
// display and sorting might depend on the number of results
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
e.message = `Error running query: ${e.message}`;
|
||||||
item.failureReason = e.message;
|
item.failureReason = e.message;
|
||||||
throw e;
|
throw e;
|
||||||
} finally {
|
} finally {
|
||||||
@@ -638,7 +640,10 @@ async function activateWithInstalledDistribution(
|
|||||||
{
|
{
|
||||||
title: 'Running query',
|
title: 'Running query',
|
||||||
cancellable: true
|
cancellable: true
|
||||||
}
|
},
|
||||||
|
|
||||||
|
// Open the query server logger on error since that's usually where the interesting errors appear.
|
||||||
|
queryServerLogger
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
interface DatabaseQuickPickItem extends QuickPickItem {
|
interface DatabaseQuickPickItem extends QuickPickItem {
|
||||||
@@ -770,7 +775,11 @@ async function activateWithInstalledDistribution(
|
|||||||
{
|
{
|
||||||
title: 'Running queries',
|
title: 'Running queries',
|
||||||
cancellable: true
|
cancellable: true
|
||||||
})
|
},
|
||||||
|
|
||||||
|
// Open the query server logger on error since that's usually where the interesting errors appear.
|
||||||
|
queryServerLogger
|
||||||
|
)
|
||||||
);
|
);
|
||||||
ctx.subscriptions.push(
|
ctx.subscriptions.push(
|
||||||
commandRunnerWithProgress(
|
commandRunnerWithProgress(
|
||||||
@@ -783,7 +792,10 @@ async function activateWithInstalledDistribution(
|
|||||||
{
|
{
|
||||||
title: 'Running query',
|
title: 'Running query',
|
||||||
cancellable: true
|
cancellable: true
|
||||||
})
|
},
|
||||||
|
// Open the query server logger on error since that's usually where the interesting errors appear.
|
||||||
|
queryServerLogger
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
ctx.subscriptions.push(
|
ctx.subscriptions.push(
|
||||||
@@ -798,7 +810,11 @@ async function activateWithInstalledDistribution(
|
|||||||
{
|
{
|
||||||
title: 'Running query',
|
title: 'Running query',
|
||||||
cancellable: true
|
cancellable: true
|
||||||
})
|
},
|
||||||
|
|
||||||
|
// Open the query server logger on error since that's usually where the interesting errors appear.
|
||||||
|
queryServerLogger
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
ctx.subscriptions.push(
|
ctx.subscriptions.push(
|
||||||
@@ -809,7 +825,10 @@ async function activateWithInstalledDistribution(
|
|||||||
displayQuickQuery(ctx, cliServer, databaseUI, progress, token),
|
displayQuickQuery(ctx, cliServer, databaseUI, progress, token),
|
||||||
{
|
{
|
||||||
title: 'Run Quick Query'
|
title: 'Run Quick Query'
|
||||||
}
|
},
|
||||||
|
|
||||||
|
// Open the query server logger on error since that's usually where the interesting errors appear.
|
||||||
|
queryServerLogger
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1003,18 +1022,23 @@ async function activateWithInstalledDistribution(
|
|||||||
|
|
||||||
// Jump-to-definition and find-references
|
// Jump-to-definition and find-references
|
||||||
void logger.log('Registering jump-to-definition handlers.');
|
void logger.log('Registering jump-to-definition handlers.');
|
||||||
|
|
||||||
|
// Store contextual queries in a temporary folder so that they are removed
|
||||||
|
// when the application closes. There is no need for the user to interact with them.
|
||||||
|
const contextualQueryStorageDir = path.join(tmpDir.name, 'contextual-query-storage');
|
||||||
|
await fs.ensureDir(contextualQueryStorageDir);
|
||||||
languages.registerDefinitionProvider(
|
languages.registerDefinitionProvider(
|
||||||
{ scheme: archiveFilesystemProvider.zipArchiveScheme },
|
{ scheme: archiveFilesystemProvider.zipArchiveScheme },
|
||||||
new TemplateQueryDefinitionProvider(cliServer, qs, dbm, queryStorageDir)
|
new TemplateQueryDefinitionProvider(cliServer, qs, dbm, contextualQueryStorageDir)
|
||||||
);
|
);
|
||||||
|
|
||||||
languages.registerReferenceProvider(
|
languages.registerReferenceProvider(
|
||||||
{ scheme: archiveFilesystemProvider.zipArchiveScheme },
|
{ scheme: archiveFilesystemProvider.zipArchiveScheme },
|
||||||
new TemplateQueryReferenceProvider(cliServer, qs, dbm, queryStorageDir)
|
new TemplateQueryReferenceProvider(cliServer, qs, dbm, contextualQueryStorageDir)
|
||||||
);
|
);
|
||||||
|
|
||||||
const astViewer = new AstViewer();
|
const astViewer = new AstViewer();
|
||||||
const templateProvider = new TemplatePrintAstProvider(cliServer, qs, dbm, queryStorageDir);
|
const templateProvider = new TemplatePrintAstProvider(cliServer, qs, dbm, contextualQueryStorageDir);
|
||||||
|
|
||||||
ctx.subscriptions.push(astViewer);
|
ctx.subscriptions.push(astViewer);
|
||||||
ctx.subscriptions.push(commandRunnerWithProgress('codeQL.viewAst', async (
|
ctx.subscriptions.push(commandRunnerWithProgress('codeQL.viewAst', async (
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export class AnalysesResultsManager {
|
|||||||
analysisSummary: AnalysisSummary,
|
analysisSummary: AnalysisSummary,
|
||||||
publishResults: (analysesResults: AnalysisResults[]) => Promise<void>
|
publishResults: (analysesResults: AnalysisResults[]) => Promise<void>
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (this.analysesResults.some(x => x.nwo === analysisSummary.nwo)) {
|
if (this.isAnalysisDownloaded(analysisSummary)) {
|
||||||
// We already have the results for this analysis, don't download again.
|
// We already have the results for this analysis, don't download again.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -38,10 +38,13 @@ export class AnalysesResultsManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async downloadAnalysesResults(
|
public async downloadAnalysesResults(
|
||||||
analysesToDownload: AnalysisSummary[],
|
allAnalysesToDownload: AnalysisSummary[],
|
||||||
token: CancellationToken | undefined,
|
token: CancellationToken | undefined,
|
||||||
publishResults: (analysesResults: AnalysisResults[]) => Promise<void>
|
publishResults: (analysesResults: AnalysisResults[]) => Promise<void>
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
// Filter out analyses that we have already downloaded.
|
||||||
|
const analysesToDownload = allAnalysesToDownload.filter(x => !this.isAnalysisDownloaded(x));
|
||||||
|
|
||||||
const credentials = await Credentials.initialize(this.ctx);
|
const credentials = await Credentials.initialize(this.ctx);
|
||||||
|
|
||||||
void this.logger.log('Downloading and processing analyses results');
|
void this.logger.log('Downloading and processing analyses results');
|
||||||
@@ -134,4 +137,8 @@ export class AnalysesResultsManager {
|
|||||||
|
|
||||||
return queryResults;
|
return queryResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private isAnalysisDownloaded(analysis: AnalysisSummary): boolean {
|
||||||
|
return this.analysesResults.some(x => x.nwo === analysis.nwo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ import { logger } from '../logging';
|
|||||||
import { RemoteQueryWorkflowResult } from './remote-query-workflow-result';
|
import { RemoteQueryWorkflowResult } from './remote-query-workflow-result';
|
||||||
import { DownloadLink } from './download-link';
|
import { DownloadLink } from './download-link';
|
||||||
import { RemoteQuery } from './remote-query';
|
import { RemoteQuery } from './remote-query';
|
||||||
import { RemoteQueryResultIndex, RemoteQueryResultIndexItem } from './remote-query-result-index';
|
import { RemoteQueryFailureIndexItem, RemoteQueryResultIndex, RemoteQuerySuccessIndexItem } from './remote-query-result-index';
|
||||||
|
|
||||||
interface ApiResultIndexItem {
|
interface ApiSuccessIndexItem {
|
||||||
nwo: string;
|
nwo: string;
|
||||||
id: string;
|
id: string;
|
||||||
results_count: number;
|
results_count: number;
|
||||||
@@ -17,6 +17,17 @@ interface ApiResultIndexItem {
|
|||||||
sarif_file_size?: number;
|
sarif_file_size?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ApiFailureIndexItem {
|
||||||
|
nwo: string;
|
||||||
|
id: string;
|
||||||
|
error: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ApiResultIndex {
|
||||||
|
successes: ApiSuccessIndexItem[];
|
||||||
|
failures: ApiFailureIndexItem[];
|
||||||
|
}
|
||||||
|
|
||||||
export async function getRemoteQueryIndex(
|
export async function getRemoteQueryIndex(
|
||||||
credentials: Credentials,
|
credentials: Credentials,
|
||||||
remoteQuery: RemoteQuery
|
remoteQuery: RemoteQuery
|
||||||
@@ -31,9 +42,9 @@ export async function getRemoteQueryIndex(
|
|||||||
|
|
||||||
const artifactList = await listWorkflowRunArtifacts(credentials, owner, repoName, workflowRunId);
|
const artifactList = await listWorkflowRunArtifacts(credentials, owner, repoName, workflowRunId);
|
||||||
const resultIndexArtifactId = getArtifactIDfromName('result-index', workflowUri, artifactList);
|
const resultIndexArtifactId = getArtifactIDfromName('result-index', workflowUri, artifactList);
|
||||||
const resultIndexItems = await getResultIndexItems(credentials, owner, repoName, resultIndexArtifactId);
|
const resultIndex = await getResultIndex(credentials, owner, repoName, resultIndexArtifactId);
|
||||||
|
|
||||||
const items = resultIndexItems.map(item => {
|
const successes = resultIndex?.successes.map(item => {
|
||||||
const artifactId = getArtifactIDfromName(item.id, workflowUri, artifactList);
|
const artifactId = getArtifactIDfromName(item.id, workflowUri, artifactList);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -42,13 +53,22 @@ export async function getRemoteQueryIndex(
|
|||||||
nwo: item.nwo,
|
nwo: item.nwo,
|
||||||
resultCount: item.results_count,
|
resultCount: item.results_count,
|
||||||
bqrsFileSize: item.bqrs_file_size,
|
bqrsFileSize: item.bqrs_file_size,
|
||||||
sarifFileSize: item.sarif_file_size,
|
sarifFileSize: item.sarif_file_size
|
||||||
} as RemoteQueryResultIndexItem;
|
} as RemoteQuerySuccessIndexItem;
|
||||||
|
});
|
||||||
|
|
||||||
|
const failures = resultIndex?.failures.map(item => {
|
||||||
|
return {
|
||||||
|
id: item.id.toString(),
|
||||||
|
nwo: item.nwo,
|
||||||
|
error: item.error
|
||||||
|
} as RemoteQueryFailureIndexItem;
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
artifactsUrlPath,
|
artifactsUrlPath,
|
||||||
items
|
successes: successes || [],
|
||||||
|
failures: failures || []
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,17 +101,17 @@ export async function downloadArtifactFromLink(
|
|||||||
* @param workflowRunId The ID of the workflow run to get the result index for.
|
* @param workflowRunId The ID of the workflow run to get the result index for.
|
||||||
* @returns An object containing the result index.
|
* @returns An object containing the result index.
|
||||||
*/
|
*/
|
||||||
async function getResultIndexItems(
|
async function getResultIndex(
|
||||||
credentials: Credentials,
|
credentials: Credentials,
|
||||||
owner: string,
|
owner: string,
|
||||||
repo: string,
|
repo: string,
|
||||||
artifactId: number
|
artifactId: number
|
||||||
): Promise<ApiResultIndexItem[]> {
|
): Promise<ApiResultIndex | undefined> {
|
||||||
const artifactPath = await downloadArtifact(credentials, owner, repo, artifactId);
|
const artifactPath = await downloadArtifact(credentials, owner, repo, artifactId);
|
||||||
const indexFilePath = path.join(artifactPath, 'index.json');
|
const indexFilePath = path.join(artifactPath, 'index.json');
|
||||||
if (!(await fs.pathExists(indexFilePath))) {
|
if (!(await fs.pathExists(indexFilePath))) {
|
||||||
void showAndLogWarningMessage('Could not find an `index.json` file in the result artifact.');
|
void showAndLogWarningMessage('Could not find an `index.json` file in the result artifact.');
|
||||||
return [];
|
return undefined;
|
||||||
}
|
}
|
||||||
const resultIndex = await fs.readFile(path.join(artifactPath, 'index.json'), 'utf8');
|
const resultIndex = await fs.readFile(path.join(artifactPath, 'index.json'), 'utf8');
|
||||||
|
|
||||||
|
|||||||
@@ -82,7 +82,8 @@ export class RemoteQueriesInterfaceManager {
|
|||||||
totalResultCount: totalResultCount,
|
totalResultCount: totalResultCount,
|
||||||
executionTimestamp: this.formatDate(query.executionStartTime),
|
executionTimestamp: this.formatDate(query.executionStartTime),
|
||||||
executionDuration: executionDuration,
|
executionDuration: executionDuration,
|
||||||
analysisSummaries: analysisSummaries
|
analysisSummaries: analysisSummaries,
|
||||||
|
analysisFailures: queryResult.analysisFailures,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -139,7 +139,8 @@ export class RemoteQueriesManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private mapQueryResult(executionEndTime: Date, resultIndex: RemoteQueryResultIndex, queryId: string): RemoteQueryResult {
|
private mapQueryResult(executionEndTime: Date, resultIndex: RemoteQueryResultIndex, queryId: string): RemoteQueryResult {
|
||||||
const analysisSummaries = resultIndex.items.map(item => ({
|
|
||||||
|
const analysisSummaries = resultIndex.successes.map(item => ({
|
||||||
nwo: item.nwo,
|
nwo: item.nwo,
|
||||||
resultCount: item.resultCount,
|
resultCount: item.resultCount,
|
||||||
fileSizeInBytes: item.sarifFileSize ? item.sarifFileSize : item.bqrsFileSize,
|
fileSizeInBytes: item.sarifFileSize ? item.sarifFileSize : item.bqrsFileSize,
|
||||||
@@ -150,10 +151,15 @@ export class RemoteQueriesManager {
|
|||||||
queryId,
|
queryId,
|
||||||
} as DownloadLink
|
} as DownloadLink
|
||||||
}));
|
}));
|
||||||
|
const analysisFailures = resultIndex.failures.map(item => ({
|
||||||
|
nwo: item.nwo,
|
||||||
|
error: item.error
|
||||||
|
}));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
executionEndTime,
|
executionEndTime,
|
||||||
analysisSummaries,
|
analysisSummaries,
|
||||||
|
analysisFailures
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
export interface RemoteQueryResultIndex {
|
export interface RemoteQueryResultIndex {
|
||||||
artifactsUrlPath: string;
|
artifactsUrlPath: string;
|
||||||
items: RemoteQueryResultIndexItem[];
|
successes: RemoteQuerySuccessIndexItem[];
|
||||||
|
failures: RemoteQueryFailureIndexItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RemoteQueryResultIndexItem {
|
export interface RemoteQuerySuccessIndexItem {
|
||||||
id: string;
|
id: string;
|
||||||
artifactId: number;
|
artifactId: number;
|
||||||
nwo: string;
|
nwo: string;
|
||||||
@@ -11,3 +12,10 @@ export interface RemoteQueryResultIndexItem {
|
|||||||
bqrsFileSize: number;
|
bqrsFileSize: number;
|
||||||
sarifFileSize?: number;
|
sarifFileSize?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RemoteQueryFailureIndexItem {
|
||||||
|
id: string;
|
||||||
|
artifactId: number;
|
||||||
|
nwo: string;
|
||||||
|
error: string;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import { DownloadLink } from './download-link';
|
import { DownloadLink } from './download-link';
|
||||||
|
import { AnalysisFailure } from './shared/analysis-failure';
|
||||||
|
|
||||||
export interface RemoteQueryResult {
|
export interface RemoteQueryResult {
|
||||||
executionEndTime: Date;
|
executionEndTime: Date;
|
||||||
analysisSummaries: AnalysisSummary[];
|
analysisSummaries: AnalysisSummary[];
|
||||||
|
analysisFailures: AnalysisFailure[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AnalysisSummary {
|
export interface AnalysisSummary {
|
||||||
|
|||||||
@@ -166,6 +166,9 @@ async function generateQueryPack(cliServer: cli.CodeQLCliServer, queryFile: stri
|
|||||||
|
|
||||||
await ensureNameAndSuite(queryPackDir, packRelativePath);
|
await ensureNameAndSuite(queryPackDir, packRelativePath);
|
||||||
|
|
||||||
|
// Clear the cliServer cache so that the previous qlpack text is purged from the CLI.
|
||||||
|
await cliServer.clearCache();
|
||||||
|
|
||||||
const bundlePath = await getPackedBundlePath(queryPackDir);
|
const bundlePath = await getPackedBundlePath(queryPackDir);
|
||||||
void logger.log(`Compiling and bundling query pack from ${queryPackDir} to ${bundlePath}. (This may take a while.)`);
|
void logger.log(`Compiling and bundling query pack from ${queryPackDir} to ${bundlePath}. (This may take a while.)`);
|
||||||
await cliServer.packInstall(queryPackDir);
|
await cliServer.packInstall(queryPackDir);
|
||||||
@@ -379,16 +382,12 @@ export async function attemptRerun(
|
|||||||
) {
|
) {
|
||||||
if (typeof error.message === 'string' && error.message.includes('Some repositories were invalid')) {
|
if (typeof error.message === 'string' && error.message.includes('Some repositories were invalid')) {
|
||||||
const invalidRepos = error?.response?.data?.invalid_repos || [];
|
const invalidRepos = error?.response?.data?.invalid_repos || [];
|
||||||
const reposWithoutDbUploads = error?.response?.data?.repos_without_db_uploads || [];
|
|
||||||
void logger.log('Unable to run query on some of the specified repositories');
|
void logger.log('Unable to run query on some of the specified repositories');
|
||||||
if (invalidRepos.length > 0) {
|
if (invalidRepos.length > 0) {
|
||||||
void logger.log(`Invalid repos: ${invalidRepos.join(', ')}`);
|
void logger.log(`Invalid repos: ${invalidRepos.join(', ')}`);
|
||||||
}
|
}
|
||||||
if (reposWithoutDbUploads.length > 0) {
|
|
||||||
void logger.log(`Repos without DB uploads: ${reposWithoutDbUploads.join(', ')}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (invalidRepos.length + reposWithoutDbUploads.length === repositories.length) {
|
if (invalidRepos.length === repositories.length) {
|
||||||
// Every repo is invalid in some way
|
// Every repo is invalid in some way
|
||||||
void showAndLogErrorMessage('Unable to run query on any of the specified repositories.');
|
void showAndLogErrorMessage('Unable to run query on any of the specified repositories.');
|
||||||
return;
|
return;
|
||||||
@@ -397,7 +396,7 @@ export async function attemptRerun(
|
|||||||
const popupMessage = 'Unable to run query on some of the specified repositories. [See logs for more details](command:codeQL.showLogs).';
|
const popupMessage = 'Unable to run query on some of the specified repositories. [See logs for more details](command:codeQL.showLogs).';
|
||||||
const rerunQuery = await showInformationMessageWithAction(popupMessage, 'Rerun on the valid repositories only');
|
const rerunQuery = await showInformationMessageWithAction(popupMessage, 'Rerun on the valid repositories only');
|
||||||
if (rerunQuery) {
|
if (rerunQuery) {
|
||||||
const validRepositories = repositories.filter(r => !invalidRepos.includes(r) && !reposWithoutDbUploads.includes(r));
|
const validRepositories = repositories.filter(r => !invalidRepos.includes(r));
|
||||||
void logger.log(`Rerunning query on set of valid repositories: ${JSON.stringify(validRepositories)}`);
|
void logger.log(`Rerunning query on set of valid repositories: ${JSON.stringify(validRepositories)}`);
|
||||||
return await runRemoteQueriesApiRequest(credentials, ref, language, validRepositories, owner, repo, queryPackBase64, dryRun);
|
return await runRemoteQueriesApiRequest(credentials, ref, language, validRepositories, owner, repo, queryPackBase64, dryRun);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,6 +83,16 @@ export const sampleRemoteQueryResult: RemoteQueryResult = {
|
|||||||
queryId: 'query.ql-123-xyz'
|
queryId: 'query.ql-123-xyz'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
analysisFailures: [
|
||||||
|
{
|
||||||
|
nwo: 'big-corp/repo5',
|
||||||
|
error: 'Error message'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
nwo: 'big-corp/repo6',
|
||||||
|
error: 'Error message'
|
||||||
|
},
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export interface AnalysisFailure {
|
||||||
|
nwo: string,
|
||||||
|
error: string
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { DownloadLink } from '../download-link';
|
import { DownloadLink } from '../download-link';
|
||||||
|
import { AnalysisFailure } from './analysis-failure';
|
||||||
|
|
||||||
export interface RemoteQueryResult {
|
export interface RemoteQueryResult {
|
||||||
queryTitle: string;
|
queryTitle: string;
|
||||||
@@ -10,7 +11,8 @@ export interface RemoteQueryResult {
|
|||||||
totalResultCount: number;
|
totalResultCount: number;
|
||||||
executionTimestamp: string;
|
executionTimestamp: string;
|
||||||
executionDuration: string;
|
executionDuration: string;
|
||||||
analysisSummaries: AnalysisSummary[]
|
analysisSummaries: AnalysisSummary[],
|
||||||
|
analysisFailures: AnalysisFailure[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AnalysisSummary {
|
export interface AnalysisSummary {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as octicons from '../../view/octicons';
|
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
import { DownloadIcon } from '@primer/octicons-react';
|
||||||
|
|
||||||
const ButtonLink = styled.a`
|
const ButtonLink = styled.a`
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@@ -16,7 +16,7 @@ const ButtonLink = styled.a`
|
|||||||
|
|
||||||
const DownloadButton = ({ text, onClick }: { text: string, onClick: () => void }) => (
|
const DownloadButton = ({ text, onClick }: { text: string, onClick: () => void }) => (
|
||||||
<ButtonLink onClick={onClick}>
|
<ButtonLink onClick={onClick}>
|
||||||
{octicons.download}{text}
|
<DownloadIcon size={16} />{text}
|
||||||
</ButtonLink>
|
</ButtonLink>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import * as Rdom from 'react-dom';
|
import * as Rdom from 'react-dom';
|
||||||
import { ThemeProvider } from '@primer/react';
|
import { Flash, ThemeProvider } from '@primer/react';
|
||||||
import { ToRemoteQueriesMessage } from '../../pure/interface-types';
|
import { ToRemoteQueriesMessage } from '../../pure/interface-types';
|
||||||
import { AnalysisSummary, RemoteQueryResult } from '../shared/remote-query-result';
|
import { AnalysisSummary, RemoteQueryResult } from '../shared/remote-query-result';
|
||||||
import * as octicons from '../../view/octicons';
|
|
||||||
|
|
||||||
import { vscode } from '../../view/vscode-api';
|
import { vscode } from '../../view/vscode-api';
|
||||||
|
|
||||||
@@ -17,7 +16,7 @@ import DownloadButton from './DownloadButton';
|
|||||||
import { AnalysisResults } from '../shared/analysis-result';
|
import { AnalysisResults } from '../shared/analysis-result';
|
||||||
import DownloadSpinner from './DownloadSpinner';
|
import DownloadSpinner from './DownloadSpinner';
|
||||||
import CollapsibleItem from './CollapsibleItem';
|
import CollapsibleItem from './CollapsibleItem';
|
||||||
import { FileSymlinkFileIcon } from '@primer/octicons-react';
|
import { AlertIcon, CodeSquareIcon, FileCodeIcon, FileSymlinkFileIcon, RepoIcon } from '@primer/octicons-react';
|
||||||
|
|
||||||
const numOfReposInContractedMode = 10;
|
const numOfReposInContractedMode = 10;
|
||||||
|
|
||||||
@@ -31,7 +30,8 @@ const emptyQueryResult: RemoteQueryResult = {
|
|||||||
totalResultCount: 0,
|
totalResultCount: 0,
|
||||||
executionTimestamp: '',
|
executionTimestamp: '',
|
||||||
executionDuration: '',
|
executionDuration: '',
|
||||||
analysisSummaries: []
|
analysisSummaries: [],
|
||||||
|
analysisFailures: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
const downloadAnalysisResults = (analysisSummary: AnalysisSummary) => {
|
const downloadAnalysisResults = (analysisSummary: AnalysisSummary) => {
|
||||||
@@ -78,12 +78,14 @@ const QueryInfo = (queryResult: RemoteQueryResult) => (
|
|||||||
{queryResult.totalResultCount} results from running against {queryResult.totalRepositoryCount} repositories
|
{queryResult.totalResultCount} results from running against {queryResult.totalRepositoryCount} repositories
|
||||||
({queryResult.executionDuration}), {queryResult.executionTimestamp}
|
({queryResult.executionDuration}), {queryResult.executionTimestamp}
|
||||||
<VerticalSpace size={1} />
|
<VerticalSpace size={1} />
|
||||||
<span className="vscode-codeql__query-file">{octicons.file}
|
<span className="vscode-codeql__query-file">
|
||||||
|
<FileCodeIcon size={16} />
|
||||||
<a className="vscode-codeql__query-file-link" href="#" onClick={() => openQueryFile(queryResult)}>
|
<a className="vscode-codeql__query-file-link" href="#" onClick={() => openQueryFile(queryResult)}>
|
||||||
{queryResult.queryFileName}
|
{queryResult.queryFileName}
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
<span>{octicons.codeSquare}
|
<span>
|
||||||
|
<CodeSquareIcon size={16} />
|
||||||
<a className="vscode-codeql__query-file-link" href="#" onClick={() => openQueryTextVirtualFile(queryResult)}>
|
<a className="vscode-codeql__query-file-link" href="#" onClick={() => openQueryTextVirtualFile(queryResult)}>
|
||||||
query
|
query
|
||||||
</a>
|
</a>
|
||||||
@@ -91,6 +93,31 @@ const QueryInfo = (queryResult: RemoteQueryResult) => (
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const Failures = (queryResult: RemoteQueryResult) => {
|
||||||
|
if (queryResult.analysisFailures.length === 0) {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<VerticalSpace size={3} />
|
||||||
|
<Flash variant="danger">
|
||||||
|
{queryResult.analysisFailures.map((f, i) => (
|
||||||
|
<>
|
||||||
|
<p className="vscode-codeql__analysis-failure" key={i}>
|
||||||
|
<AlertIcon size={16} />
|
||||||
|
<b>{f.nwo}: </b>
|
||||||
|
{f.error}
|
||||||
|
</p>
|
||||||
|
{
|
||||||
|
i === queryResult.analysisFailures.length - 1 ? <></> : <VerticalSpace size={1} />
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
))}
|
||||||
|
</Flash>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const SummaryTitleWithResults = ({
|
const SummaryTitleWithResults = ({
|
||||||
queryResult,
|
queryResult,
|
||||||
analysesResults
|
analysesResults
|
||||||
@@ -155,7 +182,7 @@ const SummaryItem = ({
|
|||||||
analysisResults: AnalysisResults | undefined
|
analysisResults: AnalysisResults | undefined
|
||||||
}) => (
|
}) => (
|
||||||
<span>
|
<span>
|
||||||
<span className="vscode-codeql__analysis-item">{octicons.repo}</span>
|
<span className="vscode-codeql__analysis-item"><RepoIcon size={16} /></span>
|
||||||
<span className="vscode-codeql__analysis-item">{analysisSummary.nwo}</span>
|
<span className="vscode-codeql__analysis-item">{analysisSummary.nwo}</span>
|
||||||
<span className="vscode-codeql__analysis-item"><Badge text={analysisSummary.resultCount.toString()} /></span>
|
<span className="vscode-codeql__analysis-item"><Badge text={analysisSummary.resultCount.toString()} /></span>
|
||||||
<span className="vscode-codeql__analysis-item">
|
<span className="vscode-codeql__analysis-item">
|
||||||
@@ -293,9 +320,10 @@ export function RemoteQueries(): JSX.Element {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
return <div>
|
return <div>
|
||||||
<ThemeProvider>
|
<ThemeProvider colorMode="auto">
|
||||||
<ViewTitle>{queryResult.queryTitle}</ViewTitle>
|
<ViewTitle>{queryResult.queryTitle}</ViewTitle>
|
||||||
<QueryInfo {...queryResult} />
|
<QueryInfo {...queryResult} />
|
||||||
|
<Failures {...queryResult} />
|
||||||
<Summary queryResult={queryResult} analysesResults={analysesResults} />
|
<Summary queryResult={queryResult} analysesResults={analysesResults} />
|
||||||
{showAnalysesResults && <AnalysesResults analysesResults={analysesResults} totalResults={queryResult.totalResultCount} />}
|
{showAnalysesResults && <AnalysesResults analysesResults={analysesResults} totalResults={queryResult.totalResultCount} />}
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
|
|||||||
@@ -1,15 +1,3 @@
|
|||||||
.octicon {
|
|
||||||
fill: var(--vscode-editor-foreground);
|
|
||||||
height: 1.2em;
|
|
||||||
width: 1.2em;
|
|
||||||
vertical-align: middle;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.octicon-light {
|
|
||||||
opacity: 0.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vscode-codeql__query-file {
|
.vscode-codeql__query-file {
|
||||||
padding-right: 1em;
|
padding-right: 1em;
|
||||||
}
|
}
|
||||||
@@ -64,3 +52,10 @@
|
|||||||
.vscode-codeql__analysis-result-file-link {
|
.vscode-codeql__analysis-result-file-link {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vscode-codeql__analysis-failure {
|
||||||
|
margin: 0;
|
||||||
|
font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas,
|
||||||
|
Liberation Mono, monospace;
|
||||||
|
color: var(--vscode-editor-foreground);
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,23 +20,3 @@ export const listUnordered = <svg className="octicon octicon-light" width="16" h
|
|||||||
export const info = <svg className="octicon octicon-light" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" >
|
export const info = <svg className="octicon octicon-light" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" >
|
||||||
<path fillRule="evenodd" clipRule="evenodd" d="M8.568 1.03a6.8 6.8 0 0 1 4.192 2.02 7.06 7.06 0 0 1 .46 9.39 6.85 6.85 0 0 1-8.58 1.74 7 7 0 0 1-3.12-3.5 7.12 7.12 0 0 1-.23-4.71 7 7 0 0 1 2.77-3.79 6.8 6.8 0 0 1 4.508-1.15zm.472 12.85a5.89 5.89 0 0 0 3.41-2.07 6.07 6.07 0 0 0-.4-8.06 5.82 5.82 0 0 0-7.43-.74 6.06 6.06 0 0 0 .5 10.29 5.81 5.81 0 0 0 3.92.58zM8.51 7h-1v4h1V7zm0-2h-1v1h1V5z" />
|
<path fillRule="evenodd" clipRule="evenodd" d="M8.568 1.03a6.8 6.8 0 0 1 4.192 2.02 7.06 7.06 0 0 1 .46 9.39 6.85 6.85 0 0 1-8.58 1.74 7 7 0 0 1-3.12-3.5 7.12 7.12 0 0 1-.23-4.71 7 7 0 0 1 2.77-3.79 6.8 6.8 0 0 1 4.508-1.15zm.472 12.85a5.89 5.89 0 0 0 3.41-2.07 6.07 6.07 0 0 0-.4-8.06 5.82 5.82 0 0 0-7.43-.74 6.06 6.06 0 0 0 .5 10.29 5.81 5.81 0 0 0 3.92.58zM8.51 7h-1v4h1V7zm0-2h-1v1h1V5z" />
|
||||||
</svg>;
|
</svg>;
|
||||||
|
|
||||||
/**
|
|
||||||
* The icons below come from https://primer.style/octicons/
|
|
||||||
*/
|
|
||||||
|
|
||||||
export const file = <svg className="octicon octicon-light" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path fillRule="evenodd" d="M3.75 1.5a.25.25 0 00-.25.25v11.5c0 .138.112.25.25.25h8.5a.25.25 0 00.25-.25V6H9.75A1.75 1.75 0 018 4.25V1.5H3.75zm5.75.56v2.19c0 .138.112.25.25.25h2.19L9.5 2.06zM2 1.75C2 .784 2.784 0 3.75 0h5.086c.464 0 .909.184 1.237.513l3.414 3.414c.329.328.513.773.513 1.237v8.086A1.75 1.75 0 0112.25 15h-8.5A1.75 1.75 0 012 13.25V1.75z"></path>
|
|
||||||
</svg>;
|
|
||||||
|
|
||||||
export const codeSquare = <svg className="octicon octicon-light" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path fillRule="evenodd" d="M1.75 1.5a.25.25 0 00-.25.25v12.5c0 .138.112.25.25.25h12.5a.25.25 0 00.25-.25V1.75a.25.25 0 00-.25-.25H1.75zM0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0114.25 16H1.75A1.75 1.75 0 010 14.25V1.75zm9.22 3.72a.75.75 0 000 1.06L10.69 8 9.22 9.47a.75.75 0 101.06 1.06l2-2a.75.75 0 000-1.06l-2-2a.75.75 0 00-1.06 0zM6.78 6.53a.75.75 0 00-1.06-1.06l-2 2a.75.75 0 000 1.06l2 2a.75.75 0 101.06-1.06L5.31 8l1.47-1.47z"></path>
|
|
||||||
</svg>;
|
|
||||||
|
|
||||||
export const repo = <svg className="octicon octicon-light" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path fillRule="evenodd" d="M2 2.5A2.5 2.5 0 014.5 0h8.75a.75.75 0 01.75.75v12.5a.75.75 0 01-.75.75h-2.5a.75.75 0 110-1.5h1.75v-2h-8a1 1 0 00-.714 1.7.75.75 0 01-1.072 1.05A2.495 2.495 0 012 11.5v-9zm10.5-1V9h-8c-.356 0-.694.074-1 .208V2.5a1 1 0 011-1h8zM5 12.25v3.25a.25.25 0 00.4.2l1.45-1.087a.25.25 0 01.3 0L8.6 15.7a.25.25 0 00.4-.2v-3.25a.25.25 0 00-.25-.25h-3.5a.25.25 0 00-.25.25z"></path>
|
|
||||||
</svg>;
|
|
||||||
|
|
||||||
export const download = <svg className="octicon octicon-light" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path fillRule="evenodd" d="M7.47 10.78a.75.75 0 001.06 0l3.75-3.75a.75.75 0 00-1.06-1.06L8.75 8.44V1.75a.75.75 0 00-1.5 0v6.69L4.78 5.97a.75.75 0 00-1.06 1.06l3.75 3.75zM3.75 13a.75.75 0 000 1.5h8.5a.75.75 0 000-1.5h-8.5z"></path>
|
|
||||||
</svg>;
|
|
||||||
|
|||||||
@@ -96,12 +96,16 @@ describe('Remote queries', function() {
|
|||||||
expect(fs.existsSync(path.join(queryPackDir, 'not-in-pack.ql'))).to.be.false;
|
expect(fs.existsSync(path.join(queryPackDir, 'not-in-pack.ql'))).to.be.false;
|
||||||
|
|
||||||
// the compiled pack
|
// the compiled pack
|
||||||
const compiledPackDir = path.join(queryPackDir, '.codeql/pack/github/remote-query-pack/0.0.0/');
|
const compiledPackDir = path.join(queryPackDir, '.codeql/pack/codeql-remote/query/0.0.0/');
|
||||||
printDirectoryContents(compiledPackDir);
|
printDirectoryContents(compiledPackDir);
|
||||||
|
|
||||||
expect(fs.existsSync(path.join(compiledPackDir, 'in-pack.ql'))).to.be.true;
|
expect(fs.existsSync(path.join(compiledPackDir, 'in-pack.ql'))).to.be.true;
|
||||||
expect(fs.existsSync(path.join(compiledPackDir, 'lib.qll'))).to.be.true;
|
expect(fs.existsSync(path.join(compiledPackDir, 'lib.qll'))).to.be.true;
|
||||||
expect(fs.existsSync(path.join(compiledPackDir, 'qlpack.yml'))).to.be.true;
|
expect(fs.existsSync(path.join(compiledPackDir, 'qlpack.yml'))).to.be.true;
|
||||||
|
// should have generated a correct qlpack file
|
||||||
|
const qlpackContents: any = yaml.safeLoad(fs.readFileSync(path.join(compiledPackDir, 'qlpack.yml'), 'utf8'));
|
||||||
|
expect(qlpackContents.name).to.equal('codeql-remote/query');
|
||||||
|
|
||||||
// depending on the cli version, we should have one of these files
|
// depending on the cli version, we should have one of these files
|
||||||
expect(
|
expect(
|
||||||
fs.existsSync(path.join(compiledPackDir, 'qlpack.lock.yml')) ||
|
fs.existsSync(path.join(compiledPackDir, 'qlpack.lock.yml')) ||
|
||||||
@@ -211,7 +215,7 @@ describe('Remote queries', function() {
|
|||||||
expect(fs.existsSync(path.join(queryPackDir, 'not-in-pack.ql'))).to.be.false;
|
expect(fs.existsSync(path.join(queryPackDir, 'not-in-pack.ql'))).to.be.false;
|
||||||
|
|
||||||
// the compiled pack
|
// the compiled pack
|
||||||
const compiledPackDir = path.join(queryPackDir, '.codeql/pack/github/remote-query-pack/0.0.0/');
|
const compiledPackDir = path.join(queryPackDir, '.codeql/pack/codeql-remote/query/0.0.0/');
|
||||||
printDirectoryContents(compiledPackDir);
|
printDirectoryContents(compiledPackDir);
|
||||||
expect(fs.existsSync(path.join(compiledPackDir, 'otherfolder/lib.qll'))).to.be.true;
|
expect(fs.existsSync(path.join(compiledPackDir, 'otherfolder/lib.qll'))).to.be.true;
|
||||||
expect(fs.existsSync(path.join(compiledPackDir, 'subfolder/in-pack.ql'))).to.be.true;
|
expect(fs.existsSync(path.join(compiledPackDir, 'subfolder/in-pack.ql'))).to.be.true;
|
||||||
|
|||||||
@@ -114,11 +114,10 @@ describe('run-remote-query', function() {
|
|||||||
let mod: any;
|
let mod: any;
|
||||||
|
|
||||||
const error = {
|
const error = {
|
||||||
message: 'Unable to run query on the specified repositories. Some repositories were invalid or don\'t have database uploads enabled.',
|
message: 'Unable to run query on the specified repositories. Some repositories were invalid.',
|
||||||
response: {
|
response: {
|
||||||
data: {
|
data: {
|
||||||
invalid_repos: ['abc/def', 'ghi/jkl'],
|
invalid_repos: ['abc/def', 'ghi/jkl']
|
||||||
repos_without_db_uploads: ['mno/pqr', 'stu/vwx']
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -151,7 +150,7 @@ describe('run-remote-query', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return and log error if it can\'t run on any repos', async () => {
|
it('should return and log error if it can\'t run on any repos', async () => {
|
||||||
const repositories = ['abc/def', 'ghi/jkl', 'mno/pqr', 'stu/vwx'];
|
const repositories = ['abc/def', 'ghi/jkl'];
|
||||||
|
|
||||||
// make the function call
|
// make the function call
|
||||||
await mod.attemptRerun(error, credentials, ref, language, repositories, query, owner, repo);
|
await mod.attemptRerun(error, credentials, ref, language, repositories, query, owner, repo);
|
||||||
@@ -159,12 +158,11 @@ describe('run-remote-query', function() {
|
|||||||
// check logging output
|
// check logging output
|
||||||
expect(logSpy.firstCall.args[0]).to.contain('Unable to run query');
|
expect(logSpy.firstCall.args[0]).to.contain('Unable to run query');
|
||||||
expect(logSpy.secondCall.args[0]).to.contain('Invalid repos: abc/def, ghi/jkl');
|
expect(logSpy.secondCall.args[0]).to.contain('Invalid repos: abc/def, ghi/jkl');
|
||||||
expect(logSpy.thirdCall.args[0]).to.contain('Repos without DB uploads: mno/pqr, stu/vwx');
|
|
||||||
expect(showAndLogErrorMessageSpy.firstCall.args[0]).to.contain('Unable to run query on any');
|
expect(showAndLogErrorMessageSpy.firstCall.args[0]).to.contain('Unable to run query on any');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should list invalid repos and repos without DB uploads, and rerun on valid ones', async () => {
|
it('should list invalid repos and rerun on valid ones', async () => {
|
||||||
const repositories = ['foo/bar', 'abc/def', 'ghi/jkl', 'mno/pqr', 'foo/baz'];
|
const repositories = ['foo/bar', 'abc/def', 'ghi/jkl', 'foo/baz'];
|
||||||
|
|
||||||
// fake return values
|
// fake return values
|
||||||
showInformationMessageWithActionSpy.resolves(true);
|
showInformationMessageWithActionSpy.resolves(true);
|
||||||
@@ -175,7 +173,6 @@ describe('run-remote-query', function() {
|
|||||||
// check logging output
|
// check logging output
|
||||||
expect(logSpy.firstCall.args[0]).to.contain('Unable to run query');
|
expect(logSpy.firstCall.args[0]).to.contain('Unable to run query');
|
||||||
expect(logSpy.secondCall.args[0]).to.contain('Invalid repos: abc/def, ghi/jkl');
|
expect(logSpy.secondCall.args[0]).to.contain('Invalid repos: abc/def, ghi/jkl');
|
||||||
expect(logSpy.thirdCall.args[0]).to.contain('Repos without DB uploads: mno/pqr');
|
|
||||||
|
|
||||||
// check that the correct information message is displayed
|
// check that the correct information message is displayed
|
||||||
expect(showInformationMessageWithActionSpy.firstCall.args[0]).to.contain('Unable to run query on some');
|
expect(showInformationMessageWithActionSpy.firstCall.args[0]).to.contain('Unable to run query on some');
|
||||||
@@ -198,8 +195,4 @@ describe('run-remote-query', function() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('runRemoteQuery', () => {
|
|
||||||
// TODO
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user