-
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 all 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,142 @@ | ||
| import { CliVersionConstraint, 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> { | ||
| if (!(await cliServer.cliConstraints.supportsPackaging())) { | ||
| throw new Error(`Packaging commands are not supported by this version of CodeQL. Please upgrade to v${CliVersionConstraint.CLI_VERSION_WITH_PACKAGING | ||
| } or later.`); | ||
| } | ||
| 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 log 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 handleInstallPackDependencies( | ||
| cliServer: CodeQLCliServer, | ||
| progress: ProgressCallback, | ||
| ): Promise<void> { | ||
| if (!(await cliServer.cliConstraints.supportsPackaging())) { | ||
| throw new Error(`Packaging commands are not supported by this version of CodeQL. Please upgrade to v${CliVersionConstraint.CLI_VERSION_WITH_PACKAGING | ||
| } or later.`); | ||
| } | ||
| progress({ | ||
| message: 'Choose packs to install dependencies for', | ||
| step: 1, | ||
| maxStep: 2, | ||
| }); | ||
| const workspacePacks = await cliServer.resolveQlpacks(getOnDiskWorkspaceFolders()); | ||
| const quickPickItems = Object.entries(workspacePacks).map<QLPackQuickPickItem>(([key, value]) => ({ | ||
| label: key, | ||
| packRootDir: value, | ||
| })); | ||
| const packsToInstall = await window.showQuickPick(quickPickItems, { | ||
| placeHolder: 'Select packs to install dependencies for', | ||
| 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 dependencies. This may take a few minutes.', | ||
| 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 pack dependencies for: ${failedPacks.join(', ')}. See log for more details.` | ||
| ); | ||
| } else { | ||
| void showAndLogInformationMessage('Finished installing pack dependencies.'); | ||
| } | ||
| } else { | ||
| throw new UserCancellationException('No packs selected.'); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| name: foo/bar | ||
| version: 0.0.0 | ||
| dependencies: | ||
| foo/baz: '*' |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| import * as sinon from 'sinon'; | ||
| import { extensions, window } from 'vscode'; | ||
| import 'mocha'; | ||
| import * as path from 'path'; | ||
|
|
||
| import * as pq from 'proxyquire'; | ||
|
|
||
| import { CliVersionConstraint, 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.' | ||
| ); | ||
| } | ||
| if (!(await cli.cliConstraints.supportsPackaging())) { | ||
| console.log(`Packaging commands are not supported on CodeQL CLI v${CliVersionConstraint.CLI_VERSION_WITH_PACKAGING | ||
| }. Skipping this test.`); | ||
| this.skip(); | ||
| } | ||
| 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 when downloading 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 install valid workspace pack', async () => { | ||
| const rootDir = path.join(__dirname, '../../../src/vscode-tests/cli-integration/data'); | ||
| quickPickSpy.resolves([ | ||
| { | ||
| label: 'integration-test-queries-javascript', | ||
| packRootDir: [rootDir], | ||
| }, | ||
| ]); | ||
|
|
||
| await mod.handleInstallPackDependencies(cli, progress); | ||
| expect(showAndLogInformationMessageSpy.firstCall.args[0]).to.contain( | ||
| 'Finished installing pack dependencies.' | ||
| ); | ||
| }); | ||
|
|
||
| it('should throw an error when installing invalid workspace pack', async () => { | ||
| const rootDir = path.join(__dirname, '../../../src/vscode-tests/cli-integration/data-invalid-pack'); | ||
| quickPickSpy.resolves([ | ||
| { | ||
| label: 'foo/bar', | ||
| packRootDir: [rootDir], | ||
| }, | ||
| ]); | ||
|
|
||
| try { | ||
| // expect this to throw an error | ||
| await mod.handleInstallPackDependencies(cli, progress); | ||
| // This line should not be reached | ||
| expect(true).to.be.false; | ||
| } catch (error) { | ||
| expect(error.message).to.contain('Unable to install pack dependencies'); | ||
| } | ||
| }); | ||
| }); |
Uh oh!
There was an error while loading. Please reload this page.