Skip to content

Commit 9c29c5c

Browse files
authored
Add ability to create repo list from MRVA results (#1403)
1 parent fd4b602 commit 9c29c5c

File tree

9 files changed

+107
-6
lines changed

9 files changed

+107
-6
lines changed

extensions/ql-vscode/package.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,10 @@
573573
"command": "codeQLQueryHistory.openOnGithub",
574574
"title": "Open Variant Analysis on GitHub"
575575
},
576+
{
577+
"command": "codeQLQueryHistory.copyRepoList",
578+
"title": "Copy Repository List"
579+
},
576580
{
577581
"command": "codeQLQueryResults.nextPathStep",
578582
"title": "CodeQL: Show Next Step on Path"
@@ -790,6 +794,11 @@
790794
"group": "9_qlCommands",
791795
"when": "viewItem == remoteResultsItem || viewItem == inProgressRemoteResultsItem || viewItem == cancelledResultsItem"
792796
},
797+
{
798+
"command": "codeQLQueryHistory.copyRepoList",
799+
"group": "9_qlCommands",
800+
"when": "viewItem == remoteResultsItem"
801+
},
793802
{
794803
"command": "codeQLTests.showOutputDifferences",
795804
"group": "qltest@1",
@@ -978,6 +987,10 @@
978987
"command": "codeQLQueryHistory.openOnGithub",
979988
"when": "false"
980989
},
990+
{
991+
"command": "codeQLQueryHistory.copyRepoList",
992+
"when": "false"
993+
},
981994
{
982995
"command": "codeQLQueryHistory.showQueryText",
983996
"when": "false"

extensions/ql-vscode/src/extension.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -886,6 +886,12 @@ async function activateWithInstalledDistribution(
886886
await rqm.monitorRemoteQuery(queryId, query, token);
887887
}));
888888

889+
ctx.subscriptions.push(
890+
commandRunner('codeQL.copyRepoList', async (queryId: string) => {
891+
await rqm.copyRemoteQueryRepoListToClipboard(queryId);
892+
})
893+
);
894+
889895
ctx.subscriptions.push(
890896
commandRunner('codeQL.autoDownloadRemoteQueryResults', async (
891897
queryResult: RemoteQueryResult,

extensions/ql-vscode/src/pure/interface-types.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,8 @@ export type FromRemoteQueriesMessage =
395395
| OpenVirtualFileMsg
396396
| RemoteQueryDownloadAnalysisResultsMessage
397397
| RemoteQueryDownloadAllAnalysesResultsMessage
398-
| RemoteQueryExportResultsMessage;
398+
| RemoteQueryExportResultsMessage
399+
| CopyRepoListMessage;
399400

400401
export type ToRemoteQueriesMessage =
401402
| SetRemoteQueryResultMessage
@@ -433,3 +434,8 @@ export interface RemoteQueryDownloadAllAnalysesResultsMessage {
433434
export interface RemoteQueryExportResultsMessage {
434435
t: 'remoteQueryExportResults';
435436
}
437+
438+
export interface CopyRepoListMessage {
439+
t: 'copyRepoList';
440+
queryId: string;
441+
}

extensions/ql-vscode/src/query-history.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,12 @@ export class QueryHistoryManager extends DisposableObject {
491491
}
492492
)
493493
);
494+
this.push(
495+
commandRunner(
496+
'codeQLQueryHistory.copyRepoList',
497+
this.handleCopyRepoList.bind(this)
498+
)
499+
);
494500

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

1059+
async handleCopyRepoList(
1060+
singleItem: QueryHistoryInfo,
1061+
multiSelect: QueryHistoryInfo[],
1062+
) {
1063+
const { finalSingleItem, finalMultiSelect } = this.determineSelection(singleItem, multiSelect);
1064+
1065+
// Remote queries only
1066+
if (!this.assertSingleQuery(finalMultiSelect) || !finalSingleItem || finalSingleItem.t !== 'remote') {
1067+
return;
1068+
}
1069+
1070+
await commands.executeCommand('codeQL.copyRepoList', finalSingleItem.queryId);
1071+
}
1072+
10531073
async getQueryText(item: QueryHistoryInfo): Promise<string> {
10541074
return item.t === 'local'
10551075
? item.initialInfo.queryText

extensions/ql-vscode/src/remote-queries/remote-queries-interface.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ export class RemoteQueriesInterfaceManager {
8080
const affectedRepositories = queryResult.analysisSummaries.filter(r => r.resultCount > 0);
8181

8282
return {
83+
queryId: queryResult.queryId,
8384
queryTitle: query.queryName,
8485
queryFileName: queryFileName,
8586
queryFilePath: query.queryFilePath,
@@ -206,6 +207,9 @@ export class RemoteQueriesInterfaceManager {
206207
case 'openVirtualFile':
207208
await this.openVirtualFile(msg.queryText);
208209
break;
210+
case 'copyRepoList':
211+
await commands.executeCommand('codeQL.copyRepoList', msg.queryId);
212+
break;
209213
case 'remoteQueryDownloadAnalysisResults':
210214
await this.downloadAnalysisResults(msg);
211215
break;

extensions/ql-vscode/src/remote-queries/remote-queries-manager.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { CancellationToken, commands, EventEmitter, ExtensionContext, Uri, window } from 'vscode';
1+
import { CancellationToken, commands, EventEmitter, ExtensionContext, Uri, env, window } from 'vscode';
22
import { nanoid } from 'nanoid';
33
import * as path from 'path';
44
import * as fs from 'fs-extra';
5+
import * as os from 'os';
56

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

194+
public async copyRemoteQueryRepoListToClipboard(queryId: string) {
195+
const queryResult = await this.getRemoteQueryResult(queryId);
196+
const repos = queryResult.analysisSummaries.map(a => a.nwo);
197+
198+
if (repos.length > 0) {
199+
const text = [
200+
'"new-repo-list": [',
201+
...repos.slice(0, -1).map(repo => ` "${repo}",`),
202+
` "${repos[repos.length - 1]}"`,
203+
']'
204+
];
205+
206+
await env.clipboard.writeText(text.join(os.EOL));
207+
}
208+
}
209+
193210
private mapQueryResult(
194211
executionEndTime: number,
195212
resultIndex: RemoteQueryResultIndex,
@@ -259,6 +276,10 @@ export class RemoteQueriesManager extends DisposableObject {
259276
await createTimestampFile(path.join(this.storagePath, queryId));
260277
}
261278

279+
private async getRemoteQueryResult(queryId: string): Promise<RemoteQueryResult> {
280+
return await this.retrieveJsonFile<RemoteQueryResult>(queryId, 'query-result.json');
281+
}
282+
262283
private async storeJsonFile<T>(queryId: string, fileName: string, obj: T): Promise<void> {
263284
const filePath = path.join(this.storagePath, queryId, fileName);
264285
await fs.writeFile(filePath, JSON.stringify(obj, null, 2), 'utf8');

extensions/ql-vscode/src/remote-queries/shared/remote-query-result.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { DownloadLink } from '../download-link';
22
import { AnalysisFailure } from './analysis-failure';
33

44
export interface RemoteQueryResult {
5+
queryId: string,
56
queryTitle: string,
67
queryFileName: string,
78
queryFilePath: string,

extensions/ql-vscode/src/remote-queries/view/RemoteQueries.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@ import RepositoriesSearch from './RepositoriesSearch';
2222
import StarCount from './StarCount';
2323
import SortRepoFilter, { Sort, sorter } from './SortRepoFilter';
2424
import LastUpdated from './LastUpdated';
25+
import RepoListCopyButton from './RepoListCopyButton';
2526

2627
const numOfReposInContractedMode = 10;
2728

2829
const emptyQueryResult: RemoteQueryResult = {
30+
queryId: '',
2931
queryTitle: '',
3032
queryFileName: '',
3133
queryFilePath: '',
@@ -149,10 +151,14 @@ const SummaryTitleWithResults = ({
149151
text="Download all"
150152
onClick={() => downloadAllAnalysesResults(queryResult)} />
151153
}
152-
<SortRepoFilter
153-
sort={sort}
154-
setSort={setSort}
155-
/>
154+
<div style={{ flexGrow: 2, textAlign: 'right' }}>
155+
<RepoListCopyButton queryResult={queryResult} />
156+
<HorizontalSpace size={1} />
157+
<SortRepoFilter
158+
sort={sort}
159+
setSort={setSort}
160+
/>
161+
</div>
156162
</div>
157163
);
158164
};
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import * as React from 'react';
2+
import { vscode } from '../../view/vscode-api';
3+
import { RemoteQueryResult } from '../shared/remote-query-result';
4+
import { CopyIcon } from '@primer/octicons-react';
5+
import { IconButton } from '@primer/react';
6+
7+
const copyRepositoryList = (queryResult: RemoteQueryResult) => {
8+
vscode.postMessage({
9+
t: 'copyRepoList',
10+
queryId: queryResult.queryId
11+
});
12+
};
13+
14+
const RepoListCopyButton = ({ queryResult }: { queryResult: RemoteQueryResult }) => (
15+
<IconButton
16+
aria-label="Copy repository list"
17+
icon={CopyIcon}
18+
variant="invisible"
19+
size="small"
20+
sx={{ 'text-align': 'right' }}
21+
onClick={() => copyRepositoryList(queryResult)} />
22+
);
23+
24+
export default RepoListCopyButton;

0 commit comments

Comments
 (0)