Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions extensions/ql-vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,10 @@
"command": "codeQLQueryHistory.openOnGithub",
"title": "Open Variant Analysis on GitHub"
},
{
"command": "codeQLQueryHistory.copyRepoList",
"title": "Copy Repository List"
},
{
"command": "codeQLQueryResults.nextPathStep",
"title": "CodeQL: Show Next Step on Path"
Expand Down Expand Up @@ -790,6 +794,11 @@
"group": "9_qlCommands",
"when": "viewItem == remoteResultsItem || viewItem == inProgressRemoteResultsItem || viewItem == cancelledResultsItem"
},
{
"command": "codeQLQueryHistory.copyRepoList",
"group": "9_qlCommands",
"when": "viewItem == remoteResultsItem"
},
{
"command": "codeQLTests.showOutputDifferences",
"group": "qltest@1",
Expand Down Expand Up @@ -978,6 +987,10 @@
"command": "codeQLQueryHistory.openOnGithub",
"when": "false"
},
{
"command": "codeQLQueryHistory.copyRepoList",
"when": "false"
},
{
"command": "codeQLQueryHistory.showQueryText",
"when": "false"
Expand Down
6 changes: 6 additions & 0 deletions extensions/ql-vscode/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -886,6 +886,12 @@ async function activateWithInstalledDistribution(
await rqm.monitorRemoteQuery(queryId, query, token);
}));

ctx.subscriptions.push(
commandRunner('codeQL.copyRepoList', async (queryId: string) => {
await rqm.copyRemoteQueryRepoListToClipboard(queryId);
})
);

ctx.subscriptions.push(
commandRunner('codeQL.autoDownloadRemoteQueryResults', async (
queryResult: RemoteQueryResult,
Expand Down
8 changes: 7 additions & 1 deletion extensions/ql-vscode/src/pure/interface-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,8 @@ export type FromRemoteQueriesMessage =
| OpenVirtualFileMsg
| RemoteQueryDownloadAnalysisResultsMessage
| RemoteQueryDownloadAllAnalysesResultsMessage
| RemoteQueryExportResultsMessage;
| RemoteQueryExportResultsMessage
| CopyRepoListMessage;

export type ToRemoteQueriesMessage =
| SetRemoteQueryResultMessage
Expand Down Expand Up @@ -433,3 +434,8 @@ export interface RemoteQueryDownloadAllAnalysesResultsMessage {
export interface RemoteQueryExportResultsMessage {
t: 'remoteQueryExportResults';
}

export interface CopyRepoListMessage {
t: 'copyRepoList';
queryId: string;
}
20 changes: 20 additions & 0 deletions extensions/ql-vscode/src/query-history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,12 @@ export class QueryHistoryManager extends DisposableObject {
}
)
);
this.push(
commandRunner(
'codeQLQueryHistory.copyRepoList',
this.handleCopyRepoList.bind(this)
)
);

// There are two configuration items that affect the query history:
// 1. The ttl for query history items.
Expand Down Expand Up @@ -1050,6 +1056,20 @@ export class QueryHistoryManager extends DisposableObject {
);
}

async handleCopyRepoList(
singleItem: QueryHistoryInfo,
multiSelect: QueryHistoryInfo[],
) {
const { finalSingleItem, finalMultiSelect } = this.determineSelection(singleItem, multiSelect);

// Remote queries only
if (!this.assertSingleQuery(finalMultiSelect) || !finalSingleItem || finalSingleItem.t !== 'remote') {
return;
}

await commands.executeCommand('codeQL.copyRepoList', finalSingleItem.queryId);
}

async getQueryText(item: QueryHistoryInfo): Promise<string> {
return item.t === 'local'
? item.initialInfo.queryText
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ export class RemoteQueriesInterfaceManager {
const affectedRepositories = queryResult.analysisSummaries.filter(r => r.resultCount > 0);

return {
queryId: queryResult.queryId,
queryTitle: query.queryName,
queryFileName: queryFileName,
queryFilePath: query.queryFilePath,
Expand Down Expand Up @@ -206,6 +207,9 @@ export class RemoteQueriesInterfaceManager {
case 'openVirtualFile':
await this.openVirtualFile(msg.queryText);
break;
case 'copyRepoList':
await commands.executeCommand('codeQL.copyRepoList', msg.queryId);
break;
case 'remoteQueryDownloadAnalysisResults':
await this.downloadAnalysisResults(msg);
break;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { CancellationToken, commands, EventEmitter, ExtensionContext, Uri, window } from 'vscode';
import { CancellationToken, commands, EventEmitter, ExtensionContext, Uri, env, window } from 'vscode';
import { nanoid } from 'nanoid';
import * as path from 'path';
import * as fs from 'fs-extra';
import * as os from 'os';

import { Credentials } from '../authentication';
import { CodeQLCliServer } from '../cli';
Expand Down Expand Up @@ -190,6 +191,22 @@ export class RemoteQueriesManager extends DisposableObject {
results => this.interfaceManager.setAnalysisResults(results, queryResult.queryId));
}

public async copyRemoteQueryRepoListToClipboard(queryId: string) {
const queryResult = await this.getRemoteQueryResult(queryId);
const repos = queryResult.analysisSummaries.map(a => a.nwo);

if (repos.length > 0) {
const text = [
'"new-repo-list": [',
...repos.slice(0, -1).map(repo => ` "${repo}",`),
` "${repos[repos.length - 1]}"`,
Comment thread
charisk marked this conversation as resolved.
']'
];

await env.clipboard.writeText(text.join(os.EOL));
}
}

private mapQueryResult(
executionEndTime: number,
resultIndex: RemoteQueryResultIndex,
Expand Down Expand Up @@ -259,6 +276,10 @@ export class RemoteQueriesManager extends DisposableObject {
await createTimestampFile(path.join(this.storagePath, queryId));
}

private async getRemoteQueryResult(queryId: string): Promise<RemoteQueryResult> {
return await this.retrieveJsonFile<RemoteQueryResult>(queryId, 'query-result.json');
}

private async storeJsonFile<T>(queryId: string, fileName: string, obj: T): Promise<void> {
const filePath = path.join(this.storagePath, queryId, fileName);
await fs.writeFile(filePath, JSON.stringify(obj, null, 2), 'utf8');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { DownloadLink } from '../download-link';
import { AnalysisFailure } from './analysis-failure';

export interface RemoteQueryResult {
queryId: string,
queryTitle: string,
queryFileName: string,
queryFilePath: string,
Expand Down
14 changes: 10 additions & 4 deletions extensions/ql-vscode/src/remote-queries/view/RemoteQueries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ import RepositoriesSearch from './RepositoriesSearch';
import StarCount from './StarCount';
import SortRepoFilter, { Sort, sorter } from './SortRepoFilter';
import LastUpdated from './LastUpdated';
import RepoListCopyButton from './RepoListCopyButton';

const numOfReposInContractedMode = 10;

const emptyQueryResult: RemoteQueryResult = {
queryId: '',
queryTitle: '',
queryFileName: '',
queryFilePath: '',
Expand Down Expand Up @@ -149,10 +151,14 @@ const SummaryTitleWithResults = ({
text="Download all"
onClick={() => downloadAllAnalysesResults(queryResult)} />
}
<SortRepoFilter
sort={sort}
setSort={setSort}
/>
<div style={{ flexGrow: 2, textAlign: 'right' }}>
<RepoListCopyButton queryResult={queryResult} />
<HorizontalSpace size={1} />
<SortRepoFilter
sort={sort}
setSort={setSort}
/>
</div>
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import * as React from 'react';
import { vscode } from '../../view/vscode-api';
import { RemoteQueryResult } from '../shared/remote-query-result';
import { CopyIcon } from '@primer/octicons-react';
import { IconButton } from '@primer/react';

const copyRepositoryList = (queryResult: RemoteQueryResult) => {
vscode.postMessage({
t: 'copyRepoList',
queryId: queryResult.queryId
});
};

const RepoListCopyButton = ({ queryResult }: { queryResult: RemoteQueryResult }) => (
<IconButton
aria-label="Copy repository list"
icon={CopyIcon}
variant="invisible"
size="small"
sx={{ 'text-align': 'right' }}
onClick={() => copyRepositoryList(queryResult)} />
);

export default RepoListCopyButton;