MRVA: Add command to export markdown results to gist

This commit is contained in:
shati-patel 2022-05-05 18:06:14 +01:00 коммит произвёл Shati Patel
Родитель 7947afb1b4
Коммит c829c30688
7 изменённых файлов: 106 добавлений и 5 удалений

Просмотреть файл

@ -301,6 +301,10 @@
"command": "codeQL.runVariantAnalysis",
"title": "CodeQL: Run Variant Analysis"
},
{
"command": "codeQL.exportVariantAnalysisResults",
"title": "CodeQL: Export Variant Analysis Results"
},
{
"command": "codeQL.runQueries",
"title": "CodeQL: Run Queries in Selected Files"
@ -837,6 +841,10 @@
"command": "codeQL.runVariantAnalysis",
"when": "config.codeQL.canary && editorLangId == ql && resourceExtname == .ql"
},
{
"command": "codeQL.exportVariantAnalysisResults",
"when": "config.codeQL.canary"
},
{
"command": "codeQL.runQueries",
"when": "false"

Просмотреть файл

@ -3,9 +3,10 @@ import * as Octokit from '@octokit/rest';
const GITHUB_AUTH_PROVIDER_ID = 'github';
// 'repo' scope should be enough for triggering workflows. For a comprehensive list, see:
// We need 'repo' scope for triggering workflows and 'gist' scope for exporting results to Gist.
// For a comprehensive list of scopes, see:
// https://docs.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps
const SCOPES = ['repo'];
const SCOPES = ['repo', 'gist'];
/**
* Handles authentication to GitHub, using the VS Code [authentication API](https://code.visualstudio.com/api/references/vscode-api#authentication).

Просмотреть файл

@ -895,6 +895,11 @@ async function activateWithInstalledDistribution(
await rqm.autoDownloadRemoteQueryResults(queryResult, token);
}));
ctx.subscriptions.push(
commandRunner('codeQL.exportVariantAnalysisResults', async () => {
await rqm.exportVariantAnalysisResults();
}));
ctx.subscriptions.push(
commandRunner(
'codeQL.openReferencedFile',

Просмотреть файл

@ -590,6 +590,10 @@ export class QueryHistoryManager extends DisposableObject {
}
}
getCurrentQueryHistoryItem(): QueryHistoryInfo | undefined {
return this.treeDataProvider.getCurrent();
}
async handleRemoveHistoryItem(
singleItem: QueryHistoryInfo,
multiSelect: QueryHistoryInfo[] = []

Просмотреть файл

@ -311,3 +311,25 @@ function getWorkflowError(conclusion: string | null): string {
return `Unexpected variant analysis execution conclusion: ${conclusion}`;
}
/**
* Creates a gist with the given description and files.
* Returns the URL of the created gist.
*/
export async function createGist(
credentials: Credentials,
description: string,
files: { [key: string]: { content: string } }
): Promise<string | undefined> {
const octokit = await credentials.getOctokit();
console.log(description, files);
const response = await octokit.request('POST /gists', {
description,
files,
public: false,
});
if (response.status >= 300) {
throw new Error(`Error exporting variant analysis results: ${response.status} ${response?.data || ''}`);
}
return response.data.html_url;
}

Просмотреть файл

@ -305,4 +305,9 @@ export class RemoteQueriesInterfaceManager {
fileSize: this.formatFileSize(analysisResult.fileSizeInBytes)
}));
}
/** Gets the current query ID */
public getCurrentQueryId(): string | undefined {
return this.currentQueryId;
}
}

Просмотреть файл

@ -5,14 +5,14 @@ import * as fs from 'fs-extra';
import { Credentials } from '../authentication';
import { CodeQLCliServer } from '../cli';
import { ProgressCallback } from '../commandRunner';
import { ProgressCallback, UserCancellationException } from '../commandRunner';
import { createTimestampFile, showAndLogErrorMessage, showAndLogInformationMessage, showInformationMessageWithAction } from '../helpers';
import { Logger } from '../logging';
import { runRemoteQuery } from './run-remote-query';
import { RemoteQueriesInterfaceManager } from './remote-queries-interface';
import { RemoteQuery } from './remote-query';
import { RemoteQueriesMonitor } from './remote-queries-monitor';
import { getRemoteQueryIndex } from './gh-actions-api-client';
import { createGist, getRemoteQueryIndex } from './gh-actions-api-client';
import { RemoteQueryResultIndex } from './remote-query-result-index';
import { RemoteQueryResult } from './remote-query-result';
import { DownloadLink } from './download-link';
@ -23,6 +23,7 @@ import { QueryHistoryManager } from '../query-history';
import { QueryStatus } from '../query-status';
import { DisposableObject } from '../pure/disposable-object';
import { QueryHistoryInfo } from '../query-results';
import { generateMarkdown } from './remote-queries-markdown-generation';
const autoDownloadMaxSize = 300 * 1024;
const autoDownloadMaxCount = 100;
@ -38,7 +39,7 @@ export class RemoteQueriesManager extends DisposableObject {
private readonly cliServer: CodeQLCliServer,
private readonly qhm: QueryHistoryManager,
private readonly storagePath: string,
logger: Logger,
private readonly logger: Logger,
) {
super();
this.analysesResultsManager = new AnalysesResultsManager(ctx, cliServer, storagePath, logger);
@ -301,4 +302,59 @@ export class RemoteQueriesManager extends DisposableObject {
queryItem.status = QueryStatus.Failed;
}
}
public async exportVariantAnalysisResults(): Promise<void> {
const queryId = this.interfaceManager.getCurrentQueryId();
const queryHistoryItem = this.qhm.getCurrentQueryHistoryItem();
if (!queryId || !queryHistoryItem || !queryHistoryItem.completed || queryHistoryItem.t !== 'remote') {
throw new Error('No variant analysis results currently open. To open results, click an item in the query history view.');
}
void this.logger.log(`Exporting variant analysis results for query: ${queryId}`);
const query = queryHistoryItem.remoteQuery;
const analysesResults = this.analysesResultsManager.getAnalysesResults(queryId);
const credentials = await Credentials.initialize(this.ctx);
const gistOption = {
label: '$(ports-open-browser-icon) Create Gist (GitHub)',
};
const localMarkdownOption = {
label: '$(markdown) Save as markdown',
};
// User selects export format in quick pick
const exportFormat = await window.showQuickPick(
[gistOption, localMarkdownOption],
{
placeHolder: 'Select export format',
canPickMany: false,
ignoreFocusOut: true,
}
);
if (!exportFormat || !exportFormat.label) {
throw new UserCancellationException('No export format selected', true);
}
if (exportFormat === gistOption) {
const description = 'CodeQL Variant Analysis Results';
const markdownFiles = generateMarkdown(query, analysesResults, 'gist');
// Convert markdownFiles to the appropriate format for uploading to gist
const gistFiles = markdownFiles.reduce((acc, cur) => {
acc[`${cur.fileName}.md`] = { content: cur.content.join('\n') };
return acc;
}, {} as { [key: string]: { content: string } });
const gistUrl = await createGist(credentials, description, gistFiles);
void showAndLogInformationMessage(`Variant analysis results exported to [gist](${gistUrl}).`);
} else if (exportFormat === localMarkdownOption) {
// TODO: Write function that creates local markdown files
// const markdownFiles = generateMarkdown(query, analysesResults, 'local');
void showAndLogInformationMessage('Local markdown export not yet available');
}
}
}