-
Notifications
You must be signed in to change notification settings - Fork 226
Add "pack install" and "pack download" commands #1076
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
74ae0bc
c3c422f
da59c42
9efa7ae
9a780c4
3c4838d
7a04ff0
5c5a1b2
c46ace4
0185c39
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,134 @@ | ||
| import { CodeQLCliServer } from './cli'; | ||
| import { | ||
| getOnDiskWorkspaceFolders, | ||
| showAndLogErrorMessage, | ||
| showAndLogInformationMessage, | ||
| } from './helpers'; | ||
| import { QuickPickItem, window } from 'vscode'; | ||
| import { ProgressCallback, UserCancellationException } from './commandRunner'; | ||
| import { logger } from './logging'; | ||
|
|
||
| const QUERY_PACKS = [ | ||
| 'codeql/cpp-queries', | ||
| 'codeql/csharp-queries', | ||
| 'codeql/go-queries', | ||
| 'codeql/java-queries', | ||
| 'codeql/javascript-queries', | ||
| 'codeql/python-queries', | ||
| 'codeql/ruby-queries', | ||
| 'codeql/csharp-solorigate-queries', | ||
| 'codeql/javascript-experimental-atm-queries', | ||
| ]; | ||
|
|
||
| /** | ||
| * 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 queryPackOption = 'Download all core query packs'; | ||
| const customPackOption = 'Download custom specified pack'; | ||
| const quickpick = await window.showQuickPick( | ||
| [queryPackOption, customPackOption], | ||
| { ignoreFocusOut: true } | ||
| ); | ||
| if (quickpick === queryPackOption) { | ||
| packsToDownload = QUERY_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 { | ||
| throw new UserCancellationException('No pack specified.'); | ||
| } | ||
| } | ||
| if (packsToDownload?.length > 0) { | ||
| progress({ | ||
| message: 'Downloading packs. This may take a few minutes.', | ||
| step: 2, | ||
| maxStep: 2, | ||
| }); | ||
| try { | ||
| await cliServer.packDownload(packsToDownload); | ||
| void showAndLogInformationMessage('Finished downloading packs.'); | ||
| } catch (error) { | ||
| void showAndLogErrorMessage( | ||
| 'Unable to download all packs. See logs for more details.' | ||
| ); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| interface QLPackQuickPickItem extends QuickPickItem { | ||
| packRootDir: string[]; | ||
| } | ||
|
|
||
| /** | ||
| * 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, | ||
| }); | ||
| const workspacePacks = await cliServer.resolveQlpacks(getOnDiskWorkspaceFolders()); | ||
|
shati-patel marked this conversation as resolved.
|
||
| const quickPickItems = Object.entries(workspacePacks).map<QLPackQuickPickItem>(([key, value]) => ({ | ||
| label: key, | ||
| packRootDir: value, | ||
| })); | ||
| const packsToInstall = await window.showQuickPick(quickPickItems, { | ||
| placeHolder: 'Select packs to install', | ||
|
shati-patel marked this conversation as resolved.
Outdated
|
||
| canPickMany: true, | ||
| ignoreFocusOut: true, | ||
| }); | ||
| if (packsToInstall && packsToInstall.length > 0) { | ||
| progress({ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 :)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 packs. This may take a few minutes.', | ||
|
shati-patel marked this conversation as resolved.
Outdated
|
||
| step: 2, | ||
| maxStep: 2, | ||
| }); | ||
| const failedPacks = []; | ||
| const errors = []; | ||
| for (const pack of packsToInstall) { | ||
| try { | ||
| for (const dir of pack.packRootDir) { | ||
| await cliServer.packInstall(dir); | ||
| } | ||
| } catch (error) { | ||
| failedPacks.push(pack.label); | ||
| errors.push(error); | ||
| } | ||
| } | ||
| if (failedPacks.length > 0) { | ||
| void logger.log(`Errors:\n${errors.join('\n')}`); | ||
| throw new Error( | ||
| `Unable to install packs: ${failedPacks.join(', ')}. See logs for more details.` | ||
|
shati-patel marked this conversation as resolved.
Outdated
|
||
| ); | ||
|
shati-patel marked this conversation as resolved.
|
||
| } else { | ||
| void showAndLogInformationMessage('Finished installing packs.'); | ||
| } | ||
| } else { | ||
| throw new UserCancellationException('No packs selected.'); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| import * as sinon from 'sinon'; | ||
| import { extensions, window } from 'vscode'; | ||
| import 'mocha'; | ||
| import * as path from 'path'; | ||
|
|
||
| import * as pq from 'proxyquire'; | ||
|
|
||
| import { CodeQLCliServer } from '../../cli'; | ||
| import { CodeQLExtensionInterface } from '../../extension'; | ||
| import { expect } from 'chai'; | ||
|
|
||
| const proxyquire = pq.noPreserveCache(); | ||
|
|
||
| describe('Packaging commands', function() { | ||
| let sandbox: sinon.SinonSandbox; | ||
|
|
||
| // up to 3 minutes per test | ||
| this.timeout(3 * 60 * 1000); | ||
|
|
||
| let cli: CodeQLCliServer; | ||
| let progress: sinon.SinonSpy; | ||
| let quickPickSpy: sinon.SinonStub; | ||
| let inputBoxSpy: sinon.SinonStub; | ||
| let showAndLogErrorMessageSpy: sinon.SinonStub; | ||
| let showAndLogInformationMessageSpy: sinon.SinonStub; | ||
| let mod: any; | ||
|
|
||
| beforeEach(async function() { | ||
| sandbox = sinon.createSandbox(); | ||
|
|
||
| const extension = await extensions | ||
| .getExtension<CodeQLExtensionInterface | Record<string, never>>( | ||
| 'GitHub.vscode-codeql' | ||
| )! | ||
| .activate(); | ||
| if ('cliServer' in extension) { | ||
| cli = extension.cliServer; | ||
| } else { | ||
| throw new Error( | ||
| 'Extension not initialized. Make sure cli is downloaded and installed properly.' | ||
| ); | ||
| } | ||
|
|
||
| progress = sandbox.spy(); | ||
| quickPickSpy = sandbox.stub(window, 'showQuickPick'); | ||
| inputBoxSpy = sandbox.stub(window, 'showInputBox'); | ||
| showAndLogErrorMessageSpy = sandbox.stub(); | ||
| showAndLogInformationMessageSpy = sandbox.stub(); | ||
| mod = proxyquire('../../packaging', { | ||
| './helpers': { | ||
| showAndLogErrorMessage: showAndLogErrorMessageSpy, | ||
| showAndLogInformationMessage: showAndLogInformationMessageSpy, | ||
| }, | ||
| }); | ||
| }); | ||
|
|
||
| afterEach(() => { | ||
| sandbox.restore(); | ||
| }); | ||
|
|
||
| it('should download all core query packs', async () => { | ||
| quickPickSpy.resolves('Download all core query packs'); | ||
|
|
||
| await mod.handleDownloadPacks(cli, progress); | ||
| expect(showAndLogInformationMessageSpy.firstCall.args[0]).to.contain( | ||
| 'Finished downloading packs.' | ||
| ); | ||
| }); | ||
|
|
||
| it('should download valid user-specified pack', async () => { | ||
| quickPickSpy.resolves('Download custom specified pack'); | ||
| inputBoxSpy.resolves('codeql/csharp-solorigate-queries'); | ||
|
|
||
| await mod.handleDownloadPacks(cli, progress); | ||
| expect(showAndLogInformationMessageSpy.firstCall.args[0]).to.contain( | ||
| 'Finished downloading packs.' | ||
| ); | ||
| }); | ||
|
|
||
| it('should show error for invalid user-specified pack', async () => { | ||
| quickPickSpy.resolves('Download custom specified pack'); | ||
| inputBoxSpy.resolves('foo/not-a-real-pack@0.0.1'); | ||
|
|
||
| await mod.handleDownloadPacks(cli, progress); | ||
|
|
||
| expect(showAndLogErrorMessageSpy.firstCall.args[0]).to.contain( | ||
| 'Unable to download all packs.' | ||
| ); | ||
| }); | ||
|
|
||
| it('should attempt to install selected workspace packs', async () => { | ||
| const rootDir = path.join(__dirname, '../../../src/vscode-tests/cli-integration/data'); | ||
| quickPickSpy.resolves([ | ||
| { | ||
| label: 'integration-test-queries-javascript', | ||
| packRootDir: [rootDir], | ||
| }, | ||
| ]); | ||
|
|
||
| try { | ||
| await mod.handleInstallPacks(cli, progress); | ||
| expect(showAndLogInformationMessageSpy.firstCall.args[0]).to.contain( | ||
| 'Finished installing packs.' | ||
| ); | ||
| } catch (error) { | ||
| expect(error.message).to.contain('Unable to install packs:'); | ||
| } | ||
|
shati-patel marked this conversation as resolved.
Outdated
|
||
| }); | ||
| }); | ||
Uh oh!
There was an error while loading. Please reload this page.