Merge branch 'main' into aeisenberg/open-query-logger

This commit is contained in:
Andrew Eisenberg
2022-02-22 09:44:41 -08:00
committed by GitHub
14 changed files with 133 additions and 69 deletions

View File

@@ -4,6 +4,7 @@
- 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

View File

@@ -514,8 +514,7 @@ export class CodeQLCliServer implements Disposable {
async resolveLibraryPath(workspaces: string[], queryPath: string): Promise<QuerySetup> {
const subcommandArgs = [
'--query', queryPath,
'--additional-packs',
workspaces.join(path.delimiter)
...this.getAdditionalPacksArg(workspaces)
];
return await this.runJsonCodeQlCliCommand<QuerySetup>(['resolve', 'library-path'], subcommandArgs, 'Resolving library paths');
}
@@ -528,8 +527,7 @@ export class CodeQLCliServer implements Disposable {
const subcommandArgs = [
'--format', 'bylanguage',
queryUri.fsPath,
'--additional-packs',
workspaces.join(path.delimiter)
...this.getAdditionalPacksArg(workspaces)
];
return JSON.parse(await this.runCodeQlCliCommand(['resolve', 'queries'], subcommandArgs, 'Resolving query by language'));
}
@@ -584,7 +582,7 @@ export class CodeQLCliServer implements Disposable {
): AsyncGenerator<TestCompleted, void, unknown> {
const subcommandArgs = this.cliConfig.additionalTestArguments.concat([
'--additional-packs', workspaces.join(path.delimiter),
...this.getAdditionalPacksArg(workspaces),
'--threads',
this.cliConfig.numberTestThreads.toString(),
...testPaths
@@ -606,8 +604,12 @@ export class CodeQLCliServer implements Disposable {
/** Resolves the ML models that should be available when evaluating a query. */
async resolveMlModels(additionalPacks: string[]): Promise<MlModelsInfo> {
return await this.runJsonCodeQlCliCommand<MlModelsInfo>(['resolve', 'ml-models'], ['--additional-packs',
additionalPacks.join(path.delimiter)], 'Resolving ML models', false);
return await this.runJsonCodeQlCliCommand<MlModelsInfo>(
['resolve', 'ml-models'],
this.getAdditionalPacksArg(additionalPacks),
'Resolving ML models',
false
);
}
/**
@@ -772,7 +774,7 @@ export class CodeQLCliServer implements Disposable {
* @returns A list of database upgrade script directories
*/
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) {
args.push('--target-dbscheme', targetDbScheme);
if (allowDowngradesIfPossible && await this.cliConstraints.supportsDowngrades()) {
@@ -794,7 +796,7 @@ export class CodeQLCliServer implements Disposable {
* @returns A dictionary mapping qlpack name to the directory it comes from
*/
resolveQlpacks(additionalPacks: string[], searchPath?: string[]): Promise<QlpacksInfo> {
const args = ['--additional-packs', additionalPacks.join(path.delimiter)];
const args = this.getAdditionalPacksArg(additionalPacks);
if (searchPath?.length) {
args.push('--search-path', path.join(...searchPath));
}
@@ -840,7 +842,7 @@ export class CodeQLCliServer implements Disposable {
* @returns A list of query files found.
*/
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) {
args.push('--search-path', path.join(...searchPath));
}
@@ -873,8 +875,7 @@ export class CodeQLCliServer implements Disposable {
'-o',
outputPath,
dir,
'--additional-packs',
workspaceFolders.join(path.delimiter)
...this.getAdditionalPacksArg(workspaceFolders)
];
if (!precompile && await this.cliConstraints.supportsNoPrecompile()) {
args.push('--no-precompile');
@@ -929,6 +930,12 @@ export class CodeQLCliServer implements Disposable {
throw new Error('No distribution found');
}
}
private getAdditionalPacksArg(paths: string[]): string[] {
return paths.length
? ['--additional-packs', paths.join(path.delimiter)]
: [];
}
}
/**

View File

@@ -7,9 +7,9 @@ import { logger } from '../logging';
import { RemoteQueryWorkflowResult } from './remote-query-workflow-result';
import { DownloadLink } from './download-link';
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;
id: string;
results_count: number;
@@ -17,6 +17,17 @@ interface ApiResultIndexItem {
sarif_file_size?: number;
}
interface ApiFailureIndexItem {
nwo: string;
id: string;
error: string;
}
interface ApiResultIndex {
successes: ApiSuccessIndexItem[];
failures: ApiFailureIndexItem[];
}
export async function getRemoteQueryIndex(
credentials: Credentials,
remoteQuery: RemoteQuery
@@ -31,9 +42,9 @@ export async function getRemoteQueryIndex(
const artifactList = await listWorkflowRunArtifacts(credentials, owner, repoName, workflowRunId);
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);
return {
@@ -42,13 +53,22 @@ export async function getRemoteQueryIndex(
nwo: item.nwo,
resultCount: item.results_count,
bqrsFileSize: item.bqrs_file_size,
sarifFileSize: item.sarif_file_size,
} as RemoteQueryResultIndexItem;
sarifFileSize: item.sarif_file_size
} as RemoteQuerySuccessIndexItem;
});
const failures = resultIndex?.failures.map(item => {
return {
id: item.id.toString(),
nwo: item.nwo,
error: item.error
} as RemoteQueryFailureIndexItem;
});
return {
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.
* @returns An object containing the result index.
*/
async function getResultIndexItems(
async function getResultIndex(
credentials: Credentials,
owner: string,
repo: string,
artifactId: number
): Promise<ApiResultIndexItem[]> {
): Promise<ApiResultIndex | undefined> {
const artifactPath = await downloadArtifact(credentials, owner, repo, artifactId);
const indexFilePath = path.join(artifactPath, 'index.json');
if (!(await fs.pathExists(indexFilePath))) {
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');

View File

@@ -82,7 +82,8 @@ export class RemoteQueriesInterfaceManager {
totalResultCount: totalResultCount,
executionTimestamp: this.formatDate(query.executionStartTime),
executionDuration: executionDuration,
analysisSummaries: analysisSummaries
analysisSummaries: analysisSummaries,
analysisFailures: queryResult.analysisFailures,
};
}

View File

@@ -128,7 +128,8 @@ export class RemoteQueriesManager {
}
private mapQueryResult(executionEndTime: Date, resultIndex: RemoteQueryResultIndex, queryId: string): RemoteQueryResult {
const analysisSummaries = resultIndex.items.map(item => ({
const analysisSummaries = resultIndex.successes.map(item => ({
nwo: item.nwo,
resultCount: item.resultCount,
fileSizeInBytes: item.sarifFileSize ? item.sarifFileSize : item.bqrsFileSize,
@@ -139,10 +140,15 @@ export class RemoteQueriesManager {
queryId,
} as DownloadLink
}));
const analysisFailures = resultIndex.failures.map(item => ({
nwo: item.nwo,
error: item.error
}));
return {
executionEndTime,
analysisSummaries,
analysisFailures
};
}

View File

@@ -1,9 +1,10 @@
export interface RemoteQueryResultIndex {
artifactsUrlPath: string;
items: RemoteQueryResultIndexItem[];
successes: RemoteQuerySuccessIndexItem[];
failures: RemoteQueryFailureIndexItem[];
}
export interface RemoteQueryResultIndexItem {
export interface RemoteQuerySuccessIndexItem {
id: string;
artifactId: number;
nwo: string;
@@ -11,3 +12,10 @@ export interface RemoteQueryResultIndexItem {
bqrsFileSize: number;
sarifFileSize?: number;
}
export interface RemoteQueryFailureIndexItem {
id: string;
artifactId: number;
nwo: string;
error: string;
}

View File

@@ -1,8 +1,10 @@
import { DownloadLink } from './download-link';
import { AnalysisFailure } from './shared/analysis-failure';
export interface RemoteQueryResult {
executionEndTime: Date;
analysisSummaries: AnalysisSummary[];
analysisFailures: AnalysisFailure[];
}
export interface AnalysisSummary {

View File

@@ -83,6 +83,16 @@ export const sampleRemoteQueryResult: RemoteQueryResult = {
queryId: 'query.ql-123-xyz'
}
}
],
analysisFailures: [
{
nwo: 'big-corp/repo5',
error: 'Error message'
},
{
nwo: 'big-corp/repo6',
error: 'Error message'
},
]
};

View File

@@ -0,0 +1,4 @@
export interface AnalysisFailure {
nwo: string,
error: string
}

View File

@@ -1,4 +1,5 @@
import { DownloadLink } from '../download-link';
import { AnalysisFailure } from './analysis-failure';
export interface RemoteQueryResult {
queryTitle: string;
@@ -10,7 +11,8 @@ export interface RemoteQueryResult {
totalResultCount: number;
executionTimestamp: string;
executionDuration: string;
analysisSummaries: AnalysisSummary[]
analysisSummaries: AnalysisSummary[],
analysisFailures: AnalysisFailure[];
}
export interface AnalysisSummary {

View File

@@ -1,6 +1,6 @@
import * as React from 'react';
import * as octicons from '../../view/octicons';
import styled from 'styled-components';
import { DownloadIcon } from '@primer/octicons-react';
const ButtonLink = styled.a`
display: inline-block;
@@ -16,7 +16,7 @@ const ButtonLink = styled.a`
const DownloadButton = ({ text, onClick }: { text: string, onClick: () => void }) => (
<ButtonLink onClick={onClick}>
{octicons.download}{text}
<DownloadIcon size={16} />{text}
</ButtonLink>
);

View File

@@ -1,10 +1,9 @@
import * as React from 'react';
import { useEffect, useState } from 'react';
import * as Rdom from 'react-dom';
import { ThemeProvider } from '@primer/react';
import { Flash, ThemeProvider } from '@primer/react';
import { ToRemoteQueriesMessage } from '../../pure/interface-types';
import { AnalysisSummary, RemoteQueryResult } from '../shared/remote-query-result';
import * as octicons from '../../view/octicons';
import { vscode } from '../../view/vscode-api';
@@ -17,7 +16,7 @@ import DownloadButton from './DownloadButton';
import { AnalysisResults } from '../shared/analysis-result';
import DownloadSpinner from './DownloadSpinner';
import CollapsibleItem from './CollapsibleItem';
import { FileSymlinkFileIcon } from '@primer/octicons-react';
import { AlertIcon, CodeSquareIcon, FileCodeIcon, FileSymlinkFileIcon, RepoIcon } from '@primer/octicons-react';
const numOfReposInContractedMode = 10;
@@ -31,7 +30,8 @@ const emptyQueryResult: RemoteQueryResult = {
totalResultCount: 0,
executionTimestamp: '',
executionDuration: '',
analysisSummaries: []
analysisSummaries: [],
analysisFailures: [],
};
const downloadAnalysisResults = (analysisSummary: AnalysisSummary) => {
@@ -78,12 +78,14 @@ const QueryInfo = (queryResult: RemoteQueryResult) => (
{queryResult.totalResultCount} results from running against {queryResult.totalRepositoryCount} repositories
({queryResult.executionDuration}), {queryResult.executionTimestamp}
<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)}>
{queryResult.queryFileName}
</a>
</span>
<span>{octicons.codeSquare}
<span>
<CodeSquareIcon size={16} />
<a className="vscode-codeql__query-file-link" href="#" onClick={() => openQueryTextVirtualFile(queryResult)}>
query
</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 = ({
queryResult,
analysesResults
@@ -155,7 +182,7 @@ const SummaryItem = ({
analysisResults: AnalysisResults | undefined
}) => (
<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"><Badge text={analysisSummary.resultCount.toString()} /></span>
<span className="vscode-codeql__analysis-item">
@@ -293,9 +320,10 @@ export function RemoteQueries(): JSX.Element {
try {
return <div>
<ThemeProvider>
<ThemeProvider colorMode="auto">
<ViewTitle>{queryResult.queryTitle}</ViewTitle>
<QueryInfo {...queryResult} />
<Failures {...queryResult} />
<Summary queryResult={queryResult} analysesResults={analysesResults} />
{showAnalysesResults && <AnalysesResults analysesResults={analysesResults} totalResults={queryResult.totalResultCount} />}
</ThemeProvider>

View File

@@ -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 {
padding-right: 1em;
}
@@ -64,3 +52,10 @@
.vscode-codeql__analysis-result-file-link {
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);
}

View File

@@ -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" >
<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>;
/**
* 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>;