Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
8 changes: 8 additions & 0 deletions extensions/ql-vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,14 @@
"command": "codeQL.clearCache",
"title": "CodeQL: Clear Cache"
},
{
"command": "codeQL.installPacks",
"title": "CodeQL: Install Packs"
Comment thread
shati-patel marked this conversation as resolved.
Outdated
},
{
"command": "codeQL.downloadPacks",
"title": "CodeQL: Download Packs"
},
{
"command": "codeQLDatabases.setCurrentDatabase",
"title": "Set Current Database"
Expand Down
8 changes: 8 additions & 0 deletions extensions/ql-vscode/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -845,6 +845,14 @@ export class CodeQLCliServer implements Disposable {
);
}

/**
* Downloads a specified pack.
* @param pack The `<package-scope/name[@version]>` of the pack to download.
*/
async packDownload(pack: string) {
return this.runJsonCodeQlCliCommand(['pack', 'download'], [pack], 'Downloading packs');
}

async packInstall(dir: string) {
return this.runJsonCodeQlCliCommand(['pack', 'install'], [dir], 'Installing pack dependencies');
}
Expand Down
21 changes: 21 additions & 0 deletions extensions/ql-vscode/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ import { RemoteQuery } from './remote-queries/remote-query';
import { URLSearchParams } from 'url';
import { RemoteQueriesInterfaceManager } from './remote-queries/remote-queries-interface';
import { sampleRemoteQuery, sampleRemoteQueryResult } from './remote-queries/sample-data';
import { handleDownloadPacks, handleInstallPacks } from './packaging';

/**
* extension.ts
Expand Down Expand Up @@ -922,6 +923,26 @@ async function activateWithInstalledDistribution(
}
}));

ctx.subscriptions.push(
commandRunnerWithProgress('codeQL.installPacks', async (
progress: ProgressCallback
) =>
await handleInstallPacks(cliServer, progress),
{
title: 'Installing packs',
}
));

ctx.subscriptions.push(
commandRunnerWithProgress('codeQL.downloadPacks', async (
progress: ProgressCallback
) =>
await handleDownloadPacks(cliServer, progress),
{
title: 'Downloading packs',
}
));

commands.registerCommand('codeQL.showLogs', () => {
logger.show();
});
Expand Down
144 changes: 144 additions & 0 deletions extensions/ql-vscode/src/packaging.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { CodeQLCliServer } from './cli';
import * as fs from 'fs-extra';
import * as path from 'path';
import {
getOnDiskWorkspaceFolders,
showAndLogErrorMessage,
showAndLogInformationMessage,
} from './helpers';
import { window } from 'vscode';
import { ProgressCallback } from './commandRunner';

const CORE_PACKS = [
'codeql/cpp-all',
'codeql/csharp-all',
'codeql/go-all',
'codeql/java-all',
'codeql/javascript-all',
'codeql/python-all',
'codeql/ruby-all',
];
Comment thread
shati-patel marked this conversation as resolved.
Outdated

/**
* Lists all workspace folders that contain a qlpack.yml file.
*
* Note: This currently only finds packs at the root of a workspace folder.
* TODO: Add support for packs in subfolders.
*/
function getWorkspacePacks(): string[] {
const packs: string[] = [];
const workspaceFolders = getOnDiskWorkspaceFolders();
for (const folder of workspaceFolders) {
const qlpackYml = path.join(folder, 'qlpack.yml');
if (fs.pathExistsSync(qlpackYml)) {
packs.push(folder);
}
}
return packs;
}
Comment thread
shati-patel marked this conversation as resolved.
Outdated

/**
* Prompts user to choose packs to download, and downloads them.
*
* @param cliServer The CLI server.
* @param progress A progress callback.
*/
export async function handleDownloadPacks(
cliServer: CodeQLCliServer,
progress: ProgressCallback,
): Promise<void> {
progress({
message: 'Choose packs to download',
step: 1,
maxStep: 2,
});
let packsToDownload: string[] = [];
const corePackOption = 'Download core CodeQL packs';
const customPackOption = 'Download custom specified pack';
const quickpick = await window.showQuickPick(
[corePackOption, customPackOption],
{ ignoreFocusOut: true }
);
if (quickpick === corePackOption) {
packsToDownload = CORE_PACKS;
} else if (quickpick === customPackOption) {
const customPack = await window.showInputBox({
prompt:
'Enter the <package-scope/name[@version]> of the pack to download',
ignoreFocusOut: true,
});
if (customPack) {
packsToDownload.push(customPack);
} else {
void showAndLogErrorMessage('No pack specified.');
}
}
if (packsToDownload && packsToDownload.length > 0) {
Comment thread
shati-patel marked this conversation as resolved.
Outdated
progress({
message: `Downloading ${packsToDownload.join(', ')}`,
step: 2,
maxStep: 2,
});
for (const pack of packsToDownload) {
try {
await cliServer.packDownload(pack);
Comment thread
shati-patel marked this conversation as resolved.
Outdated
} catch (error) {
void showAndLogErrorMessage(`Unable to download pack ${pack}. See logs for more details.`);
}
}
void showAndLogInformationMessage('Finished downloading packs.');
}
}

/**
* Prompts user to choose packs to install, and installs them.
*
* @param cliServer The CLI server.
* @param progress A progress callback.
*/
export async function handleInstallPacks(
cliServer: CodeQLCliServer,
progress: ProgressCallback,
): Promise<void> {
progress({
message: 'Choose packs to install',
step: 1,
maxStep: 2,
});
let packsToInstall: string[] = [];
const workspacePackOption = 'Install workspace packs';
const customPackOption = 'Install custom specified pack';
Comment thread
shati-patel marked this conversation as resolved.
Outdated
const quickpick = await window.showQuickPick(
[workspacePackOption, customPackOption],
{ ignoreFocusOut: true }
);
if (quickpick === workspacePackOption) {
packsToInstall = getWorkspacePacks();
} else if (quickpick === customPackOption) {
const customPack = await window.showInputBox({
prompt:
'Enter the root directory of the pack to install (as an absolute path)',
ignoreFocusOut: true,
});
if (customPack) {
packsToInstall.push(customPack);
} else {
void showAndLogErrorMessage('No pack specified.');
}
}
if (packsToInstall && packsToInstall.length > 0) {
progress({
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: You know the total number of root packs, so you could make the progress monitor actually reflect the progress through the root packs. Save that for a future PR though :)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea! I will try that in a follow-up PR 🔄

message: `Installing ${packsToInstall.join(', ')}`,
step: 2,
maxStep: 2,
});
for (const pack of packsToInstall) {
try {
await cliServer.packInstall(pack);
} catch (error) {
void showAndLogErrorMessage(`Unable to install pack ${pack}. See logs for more details.`);
}
}
void showAndLogInformationMessage('Finished installing packs.');
}
}