Skip to content

Commit 2a8e233

Browse files
committed
Remote Queries: Create packs for remote queries
This is still a bit rough, but handles two cases: 1. There is a qlpack.yml or codeql-pack.yml file in the same directory as the query to run remotely. In this case, run `codeql pack packlist` to determine what files to include (and also always include the lock file and the query itself. Copy to a temp folder and run `pack install`, then `pack bundle`. Finally upload. 2. There is no qlpack in the current directory. Just copy the single file to the temp folder and generate a synthetic qlpack before installing, bundling and uploading. Two cases that are not handled: 1. The query file is part of a workspace. Peer dependencies will not be found. 2. The query file and its qlpack file are not in the same directory. These should be possible to handle later. Also, need to create some unit and integration tests for this.
1 parent b2a6263 commit 2a8e233

File tree

15 files changed

+504
-84
lines changed

15 files changed

+504
-84
lines changed

extensions/ql-vscode/src/cli.ts

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,7 @@ export class CodeQLCliServer implements Disposable {
605605
if (target) subcommandArgs.push('--target', target);
606606
if (name) subcommandArgs.push('--name', name);
607607
subcommandArgs.push(archivePath);
608-
608+
609609
return await this.runCodeQlCliCommand(['database', 'unbundle'], subcommandArgs, `Extracting ${archivePath} to directory ${target}`);
610610
}
611611

@@ -805,8 +805,30 @@ export class CodeQLCliServer implements Disposable {
805805
return this.runJsonCodeQlCliCommand(['pack', 'install'], [dir], 'Installing pack dependencies');
806806
}
807807

808-
async packBundle(dir: string, outputPath: string): Promise<void> {
809-
return this.runJsonCodeQlCliCommand(['pack', 'bundle'], ['-o', outputPath, dir], 'Bundling pack');
808+
async packBundle(dir: string, workspaceFolders: string[], outputPath: string, precompile = true): Promise<void> {
809+
const args = [
810+
'-o',
811+
outputPath,
812+
dir,
813+
'--additional-packs',
814+
workspaceFolders.join(path.delimiter)
815+
];
816+
if (!precompile && await this.cliConstraints.supportsNoPrecompile()) {
817+
args.push('--no-precompile');
818+
}
819+
820+
return this.runJsonCodeQlCliCommand(['pack', 'bundle'], args, 'Bundling pack');
821+
}
822+
823+
async packPacklist(dir: string, includeQueries: boolean): Promise<string[]> {
824+
const args = includeQueries ? [dir] : ['--no-include-queries', dir];
825+
const results = await this.runJsonCodeQlCliCommand(['pack', 'packlist'], args, 'Generating the pack list');
826+
827+
if (await this.cliConstraints.usesNewPackPacklistLayout()) {
828+
return (results as { paths: string[] }).paths;
829+
} else {
830+
return results as string[];
831+
}
810832
}
811833

812834
async generateDil(qloFile: string, outFile: string): Promise<void> {
@@ -1057,6 +1079,12 @@ export function shouldDebugQueryServer() {
10571079
&& process.env.QUERY_SERVER_JAVA_DEBUG?.toLocaleLowerCase() !== 'false';
10581080
}
10591081

1082+
export function shouldDebugCliServer() {
1083+
return 'CLI_SERVER_JAVA_DEBUG' in process.env
1084+
&& process.env.CLI_SERVER_JAVA_DEBUG !== '0'
1085+
&& process.env.CLI_SERVER_JAVA_DEBUG?.toLocaleLowerCase() !== 'false';
1086+
}
1087+
10601088
export class CliVersionConstraint {
10611089

10621090
/**
@@ -1096,6 +1124,16 @@ export class CliVersionConstraint {
10961124
*/
10971125
public static CLI_VERSION_WITH_DATABASE_UNBUNDLE = new SemVer('2.6.0');
10981126

1127+
/**
1128+
* CLI version where the `--no-precompile` option for pack creation was introduced.
1129+
*/
1130+
public static CLI_VERSION_WITH_NO_PRECOMPILE = new SemVer('2.7.1');
1131+
1132+
/**
1133+
* CLI version where `pack packlist` layout changed from array to object
1134+
*/
1135+
public static CLI_VERSION_PACK_PACKLIST_LAYOUT_CHANGE = new SemVer('2.7.1');
1136+
10991137
constructor(private readonly cli: CodeQLCliServer) {
11001138
/**/
11011139
}
@@ -1132,4 +1170,11 @@ export class CliVersionConstraint {
11321170
return this.isVersionAtLeast(CliVersionConstraint.CLI_VERSION_WITH_DATABASE_UNBUNDLE);
11331171
}
11341172

1173+
async supportsNoPrecompile() {
1174+
return this.isVersionAtLeast(CliVersionConstraint.CLI_VERSION_WITH_NO_PRECOMPILE);
1175+
}
1176+
1177+
async usesNewPackPacklistLayout() {
1178+
return this.isVersionAtLeast(CliVersionConstraint.CLI_VERSION_PACK_PACKLIST_LAYOUT_CHANGE);
1179+
}
11351180
}

extensions/ql-vscode/src/config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ const REMOTE_QUERIES_SETTING = new Setting('remoteQueries', ROOT_SETTING);
304304
/**
305305
* Lists of GitHub repositories that you want to query remotely via the "Run Remote query" command.
306306
* Note: This command is only available for internal users.
307-
*
307+
*
308308
* This setting should be a JSON object where each key is a user-specified name (string),
309309
* and the value is an array of GitHub repositories (of the form `<owner>/<repo>`).
310310
*/

extensions/ql-vscode/src/extension.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -716,13 +716,25 @@ async function activateWithInstalledDistribution(
716716
);
717717
// The "runRemoteQuery" command is internal-only.
718718
ctx.subscriptions.push(
719-
commandRunner('codeQL.runRemoteQuery', async (
719+
commandRunnerWithProgress('codeQL.runRemoteQuery', async (
720+
progress: ProgressCallback,
721+
token: CancellationToken,
720722
uri: Uri | undefined
721723
) => {
722724
if (isCanary()) {
725+
progress({
726+
maxStep: 5,
727+
step: 0,
728+
message: 'Getting credentials'
729+
});
723730
const credentials = await Credentials.initialize(ctx);
724-
await runRemoteQuery(cliServer, credentials, uri || window.activeTextEditor?.document.uri);
731+
await runRemoteQuery(cliServer, credentials, uri || window.activeTextEditor?.document.uri, dryRun, progress, token);
732+
} else {
733+
throw new Error('Remote queries require the CodeQL Canary version to run.');
725734
}
735+
}, {
736+
title: 'Run Remote Query',
737+
cancellable: true
726738
})
727739
);
728740
ctx.subscriptions.push(

extensions/ql-vscode/src/helpers.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
env
1111
} from 'vscode';
1212
import { CodeQLCliServer, QlpacksInfo } from './cli';
13+
import { UserCancellationException } from './commandRunner';
1314
import { logger } from './logging';
1415

1516
/**
@@ -494,14 +495,25 @@ export async function findLanguage(
494495
void logger.log('Could not autodetect query language. Select language manually.');
495496
}
496497
}
497-
const availableLanguages = Object.keys(await cliServer.resolveLanguages());
498+
499+
// will be undefined if user cancels the quick pick.
500+
return await askForLanguage(cliServer, false);
501+
}
502+
503+
504+
export async function askForLanguage(cliServer: CodeQLCliServer, throwOnEmpty = true): Promise<string | undefined> {
505+
const availableLanguages = Object.keys(await cliServer.resolveLanguages()).sort();
498506
const language = await Window.showQuickPick(
499507
availableLanguages,
500508
{ placeHolder: 'Select target language for your query', ignoreFocusOut: true }
501509
);
502510
if (!language) {
503511
// This only happens if the user cancels the quick pick.
504-
void showAndLogErrorMessage('Language not found. Language must be specified manually.');
512+
if (throwOnEmpty) {
513+
throw new UserCancellationException('Cancelled.');
514+
} else {
515+
void showAndLogErrorMessage('Language not found. Language must be specified manually.');
516+
}
505517
}
506518
return language;
507519
}

0 commit comments

Comments
 (0)