Skip to content

Commit 65641e3

Browse files
committed
Move remote queries specific code out of run-remote-query
This moves some of the code that is specific to remote queries out of the `run-remote-query.ts` file and instead places it in separate files that only deal with remote queries, rather than also dealing with variant analyses.
1 parent 7649f20 commit 65641e3

File tree

6 files changed

+368
-364
lines changed

6 files changed

+368
-364
lines changed
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import * as os from 'os';
2+
import { Credentials } from '../authentication';
3+
import { RepositorySelection } from './repository-selection';
4+
import { Repository } from './shared/repository';
5+
import { RemoteQueriesResponse } from './gh-api/remote-queries';
6+
import * as ghApiClient from './gh-api/gh-api-client';
7+
import { showAndLogErrorMessage, showAndLogInformationMessage } from '../helpers';
8+
import { getErrorMessage } from '../pure/helpers-pure';
9+
import { pluralize } from '../pure/word';
10+
11+
export async function runRemoteQueriesApiRequest(
12+
credentials: Credentials,
13+
ref: string,
14+
language: string,
15+
repoSelection: RepositorySelection,
16+
controllerRepo: Repository,
17+
queryPackBase64: string,
18+
): Promise<void | RemoteQueriesResponse> {
19+
try {
20+
const response = await ghApiClient.submitRemoteQueries(credentials, {
21+
ref,
22+
language,
23+
repositories: repoSelection.repositories,
24+
repositoryLists: repoSelection.repositoryLists,
25+
repositoryOwners: repoSelection.owners,
26+
queryPack: queryPackBase64,
27+
controllerRepoId: controllerRepo.id,
28+
});
29+
const { popupMessage, logMessage } = parseResponse(controllerRepo, response);
30+
void showAndLogInformationMessage(popupMessage, { fullMessage: logMessage });
31+
return response;
32+
} catch (error: any) {
33+
if (error.status === 404) {
34+
void showAndLogErrorMessage(`Controller repository was not found. Please make sure it's a valid repo name.${eol}`);
35+
} else {
36+
void showAndLogErrorMessage(getErrorMessage(error));
37+
}
38+
}
39+
}
40+
41+
const eol = os.EOL;
42+
const eol2 = os.EOL + os.EOL;
43+
44+
// exported for testing only
45+
export function parseResponse(controllerRepo: Repository, response: RemoteQueriesResponse) {
46+
const repositoriesQueried = response.repositories_queried;
47+
const repositoryCount = repositoriesQueried.length;
48+
49+
const popupMessage = `Successfully scheduled runs on ${pluralize(repositoryCount, 'repository', 'repositories')}. [Click here to see the progress](https://github.com/${controllerRepo.fullName}/actions/runs/${response.workflow_run_id}).`
50+
+ (response.errors ? `${eol2}Some repositories could not be scheduled. See extension log for details.` : '');
51+
52+
let logMessage = `Successfully scheduled runs on ${pluralize(repositoryCount, 'repository', 'repositories')}. See https://github.com/${controllerRepo.fullName}/actions/runs/${response.workflow_run_id}.`;
53+
logMessage += `${eol2}Repositories queried:${eol}${repositoriesQueried.join(', ')}`;
54+
if (response.errors) {
55+
const { invalid_repositories, repositories_without_database, private_repositories, cutoff_repositories, cutoff_repositories_count } = response.errors;
56+
logMessage += `${eol2}Some repositories could not be scheduled.`;
57+
if (invalid_repositories?.length) {
58+
logMessage += `${eol2}${pluralize(invalid_repositories.length, 'repository', 'repositories')} invalid and could not be found:${eol}${invalid_repositories.join(', ')}`;
59+
}
60+
if (repositories_without_database?.length) {
61+
logMessage += `${eol2}${pluralize(repositories_without_database.length, 'repository', 'repositories')} did not have a CodeQL database available:${eol}${repositories_without_database.join(', ')}`;
62+
logMessage += `${eol}For each public repository that has not yet been added to the database service, we will try to create a database next time the store is updated.`;
63+
}
64+
if (private_repositories?.length) {
65+
logMessage += `${eol2}${pluralize(private_repositories.length, 'repository', 'repositories')} not public:${eol}${private_repositories.join(', ')}`;
66+
logMessage += `${eol}When using a public controller repository, only public repositories can be queried.`;
67+
}
68+
if (cutoff_repositories_count) {
69+
logMessage += `${eol2}${pluralize(cutoff_repositories_count, 'repository', 'repositories')} over the limit for a single request`;
70+
if (cutoff_repositories) {
71+
logMessage += `:${eol}${cutoff_repositories.join(', ')}`;
72+
if (cutoff_repositories_count !== cutoff_repositories.length) {
73+
const moreRepositories = cutoff_repositories_count - cutoff_repositories.length;
74+
logMessage += `${eol}...${eol}And another ${pluralize(moreRepositories, 'repository', 'repositories')}.`;
75+
}
76+
} else {
77+
logMessage += '.';
78+
}
79+
logMessage += `${eol}Repositories were selected based on how recently they had been updated.`;
80+
}
81+
}
82+
83+
return {
84+
popupMessage,
85+
logMessage
86+
};
87+
}

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,10 @@ import { ProgressCallback } from '../commandRunner';
1010
import { createTimestampFile, showAndLogErrorMessage, showAndLogInformationMessage, showInformationMessageWithAction } from '../helpers';
1111
import { Logger } from '../logging';
1212
import {
13-
buildRemoteQueryEntity,
1413
prepareRemoteQueryRun,
15-
runRemoteQueriesApiRequest,
1614
} from './run-remote-query';
1715
import { RemoteQueriesView } from './remote-queries-view';
18-
import { RemoteQuery } from './remote-query';
16+
import { buildRemoteQueryEntity, RemoteQuery } from './remote-query';
1917
import { RemoteQueriesMonitor } from './remote-queries-monitor';
2018
import { getRemoteQueryIndex, getRepositoriesMetadata, RepositoriesMetadata } from './gh-api/gh-actions-api-client';
2119
import { RemoteQueryResultIndex } from './remote-query-result-index';
@@ -26,6 +24,7 @@ import { assertNever } from '../pure/helpers-pure';
2624
import { QueryStatus } from '../query-status';
2725
import { DisposableObject } from '../pure/disposable-object';
2826
import { AnalysisResults } from './shared/analysis-result';
27+
import { runRemoteQueriesApiRequest } from './remote-queries-api';
2928

3029
const autoDownloadMaxSize = 300 * 1024;
3130
const autoDownloadMaxCount = 100;
Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,44 @@
1-
import { Repository } from './repository';
1+
import * as fs from 'fs-extra';
2+
import { Repository as RemoteRepository } from './repository';
3+
import { QueryMetadata } from '../pure/interface-types';
4+
import { getQueryName } from './run-remote-query';
5+
import { Repository } from './shared/repository';
26

37
export interface RemoteQuery {
48
queryName: string;
59
queryFilePath: string;
610
queryText: string;
711
language: string;
8-
controllerRepository: Repository;
12+
controllerRepository: RemoteRepository;
913
executionStartTime: number; // Use number here since it needs to be serialized and desserialized.
1014
actionsWorkflowRunId: number;
1115
repositoryCount: number;
1216
}
17+
18+
export async function buildRemoteQueryEntity(
19+
queryFilePath: string,
20+
queryMetadata: QueryMetadata | undefined,
21+
controllerRepo: Repository,
22+
queryStartTime: number,
23+
workflowRunId: number,
24+
language: string,
25+
repositoryCount: number
26+
): Promise<RemoteQuery> {
27+
const queryName = getQueryName(queryMetadata, queryFilePath);
28+
const queryText = await fs.readFile(queryFilePath, 'utf8');
29+
const [owner, name] = controllerRepo.fullName.split('/');
30+
31+
return {
32+
queryName,
33+
queryFilePath,
34+
queryText,
35+
language,
36+
controllerRepository: {
37+
owner,
38+
name,
39+
},
40+
executionStartTime: queryStartTime,
41+
actionsWorkflowRunId: workflowRunId,
42+
repositoryCount,
43+
};
44+
}

extensions/ql-vscode/src/remote-queries/run-remote-query.ts

Lines changed: 2 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,11 @@ import { CancellationToken, Uri, window } from 'vscode';
22
import * as path from 'path';
33
import * as yaml from 'js-yaml';
44
import * as fs from 'fs-extra';
5-
import * as os from 'os';
65
import * as tmp from 'tmp-promise';
76
import {
87
askForLanguage,
98
findLanguage,
109
getOnDiskWorkspaceFolders,
11-
showAndLogErrorMessage,
12-
showAndLogInformationMessage,
1310
tryGetQueryMetadata,
1411
tmpDir,
1512
} from '../helpers';
@@ -19,13 +16,10 @@ import { logger } from '../logging';
1916
import { getActionBranch, getRemoteControllerRepo, setRemoteControllerRepo } from '../config';
2017
import { ProgressCallback, UserCancellationException } from '../commandRunner';
2118
import { RequestError } from '@octokit/types/dist-types';
22-
import { RemoteQuery } from './remote-query';
2319
import { QueryMetadata } from '../pure/interface-types';
24-
import { getErrorMessage, REPO_REGEX } from '../pure/helpers-pure';
25-
import { pluralize } from '../pure/word';
20+
import { REPO_REGEX } from '../pure/helpers-pure';
2621
import * as ghApiClient from './gh-api/gh-api-client';
27-
import { RemoteQueriesResponse } from './gh-api/remote-queries';
28-
import { getRepositorySelection, isValidSelection, RepositorySelection } from './repository-selection';
22+
import { getRepositorySelection, isValidSelection } from './repository-selection';
2923
import { Repository } from './shared/repository';
3024

3125
export interface QlPack {
@@ -257,84 +251,6 @@ export async function prepareRemoteQueryRun(
257251
};
258252
}
259253

260-
export async function runRemoteQueriesApiRequest(
261-
credentials: Credentials,
262-
ref: string,
263-
language: string,
264-
repoSelection: RepositorySelection,
265-
controllerRepo: Repository,
266-
queryPackBase64: string,
267-
): Promise<void | RemoteQueriesResponse> {
268-
try {
269-
const response = await ghApiClient.submitRemoteQueries(credentials, {
270-
ref,
271-
language,
272-
repositories: repoSelection.repositories,
273-
repositoryLists: repoSelection.repositoryLists,
274-
repositoryOwners: repoSelection.owners,
275-
queryPack: queryPackBase64,
276-
controllerRepoId: controllerRepo.id,
277-
});
278-
const { popupMessage, logMessage } = parseResponse(controllerRepo, response);
279-
void showAndLogInformationMessage(popupMessage, { fullMessage: logMessage });
280-
return response;
281-
} catch (error: any) {
282-
if (error.status === 404) {
283-
void showAndLogErrorMessage(`Controller repository was not found. Please make sure it's a valid repo name.${eol}`);
284-
} else {
285-
void showAndLogErrorMessage(getErrorMessage(error));
286-
}
287-
}
288-
}
289-
290-
const eol = os.EOL;
291-
const eol2 = os.EOL + os.EOL;
292-
293-
// exported for testing only
294-
export function parseResponse(controllerRepo: Repository, response: RemoteQueriesResponse) {
295-
const repositoriesQueried = response.repositories_queried;
296-
const repositoryCount = repositoriesQueried.length;
297-
298-
const popupMessage = `Successfully scheduled runs on ${pluralize(repositoryCount, 'repository', 'repositories')}. [Click here to see the progress](https://github.com/${controllerRepo.fullName}/actions/runs/${response.workflow_run_id}).`
299-
+ (response.errors ? `${eol2}Some repositories could not be scheduled. See extension log for details.` : '');
300-
301-
let logMessage = `Successfully scheduled runs on ${pluralize(repositoryCount, 'repository', 'repositories')}. See https://github.com/${controllerRepo.fullName}/actions/runs/${response.workflow_run_id}.`;
302-
logMessage += `${eol2}Repositories queried:${eol}${repositoriesQueried.join(', ')}`;
303-
if (response.errors) {
304-
const { invalid_repositories, repositories_without_database, private_repositories, cutoff_repositories, cutoff_repositories_count } = response.errors;
305-
logMessage += `${eol2}Some repositories could not be scheduled.`;
306-
if (invalid_repositories?.length) {
307-
logMessage += `${eol2}${pluralize(invalid_repositories.length, 'repository', 'repositories')} invalid and could not be found:${eol}${invalid_repositories.join(', ')}`;
308-
}
309-
if (repositories_without_database?.length) {
310-
logMessage += `${eol2}${pluralize(repositories_without_database.length, 'repository', 'repositories')} did not have a CodeQL database available:${eol}${repositories_without_database.join(', ')}`;
311-
logMessage += `${eol}For each public repository that has not yet been added to the database service, we will try to create a database next time the store is updated.`;
312-
}
313-
if (private_repositories?.length) {
314-
logMessage += `${eol2}${pluralize(private_repositories.length, 'repository', 'repositories')} not public:${eol}${private_repositories.join(', ')}`;
315-
logMessage += `${eol}When using a public controller repository, only public repositories can be queried.`;
316-
}
317-
if (cutoff_repositories_count) {
318-
logMessage += `${eol2}${pluralize(cutoff_repositories_count, 'repository', 'repositories')} over the limit for a single request`;
319-
if (cutoff_repositories) {
320-
logMessage += `:${eol}${cutoff_repositories.join(', ')}`;
321-
if (cutoff_repositories_count !== cutoff_repositories.length) {
322-
const moreRepositories = cutoff_repositories_count - cutoff_repositories.length;
323-
logMessage += `${eol}...${eol}And another ${pluralize(moreRepositories, 'repository', 'repositories')}.`;
324-
}
325-
} else {
326-
logMessage += '.';
327-
}
328-
logMessage += `${eol}Repositories were selected based on how recently they had been updated.`;
329-
}
330-
}
331-
332-
return {
333-
popupMessage,
334-
logMessage
335-
};
336-
}
337-
338254
/**
339255
* Updates the default suite of the query pack. This is used to ensure
340256
* only the specified query is run.
@@ -359,34 +275,6 @@ async function ensureNameAndSuite(queryPackDir: string, packRelativePath: string
359275
await fs.writeFile(packPath, yaml.dump(qlpack));
360276
}
361277

362-
export async function buildRemoteQueryEntity(
363-
queryFilePath: string,
364-
queryMetadata: QueryMetadata | undefined,
365-
controllerRepo: Repository,
366-
queryStartTime: number,
367-
workflowRunId: number,
368-
language: string,
369-
repositoryCount: number
370-
): Promise<RemoteQuery> {
371-
const queryName = getQueryName(queryMetadata, queryFilePath);
372-
const queryText = await fs.readFile(queryFilePath, 'utf8');
373-
const [owner, name] = controllerRepo.fullName.split('/');
374-
375-
return {
376-
queryName,
377-
queryFilePath,
378-
queryText,
379-
language,
380-
controllerRepository: {
381-
owner,
382-
name,
383-
},
384-
executionStartTime: queryStartTime,
385-
actionsWorkflowRunId: workflowRunId,
386-
repositoryCount,
387-
};
388-
}
389-
390278
export function getQueryName(queryMetadata: QueryMetadata | undefined, queryFilePath: string): string {
391279
// The query name is either the name as specified in the query metadata, or the file name.
392280
return queryMetadata?.name ?? path.basename(queryFilePath);

0 commit comments

Comments
 (0)