From 0af8146f58be589b4ae414cdfad3b28cb9f573a9 Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Mon, 4 Apr 2022 11:25:49 +0100 Subject: [PATCH 1/6] Move remote queries test files to be under remote-queries dir --- .../run-remote-query.test.ts | 14 +++++----- .../remote-query-history.test.ts | 28 +++++++++---------- 2 files changed, 21 insertions(+), 21 deletions(-) rename extensions/ql-vscode/src/vscode-tests/cli-integration/{ => remote-queries}/run-remote-query.test.ts (96%) rename extensions/ql-vscode/src/vscode-tests/no-workspace/{ => remote-queries}/remote-query-history.test.ts (93%) diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/run-remote-query.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/run-remote-query.test.ts similarity index 96% rename from extensions/ql-vscode/src/vscode-tests/cli-integration/run-remote-query.test.ts rename to extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/run-remote-query.test.ts index c5a7d78de58..590c3173382 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/run-remote-query.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/run-remote-query.test.ts @@ -6,16 +6,16 @@ import * as fs from 'fs-extra'; import * as os from 'os'; import * as yaml from 'js-yaml'; -import { QlPack, runRemoteQuery } from '../../remote-queries/run-remote-query'; -import { Credentials } from '../../authentication'; -import { CliVersionConstraint, CodeQLCliServer } from '../../cli'; -import { CodeQLExtensionInterface } from '../../extension'; -import { setRemoteControllerRepo, setRemoteRepositoryLists } from '../../config'; -import { UserCancellationException } from '../../commandRunner'; +import { QlPack, runRemoteQuery } from '../../../remote-queries/run-remote-query'; +import { Credentials } from '../../../authentication'; +import { CliVersionConstraint, CodeQLCliServer } from '../../../cli'; +import { CodeQLExtensionInterface } from '../../../extension'; +import { setRemoteControllerRepo, setRemoteRepositoryLists } from '../../../config'; +import { UserCancellationException } from '../../../commandRunner'; import { lte } from 'semver'; describe('Remote queries', function() { - const baseDir = path.join(__dirname, '../../../src/vscode-tests/cli-integration'); + const baseDir = path.join(__dirname, '../../../../src/vscode-tests/cli-integration'); let sandbox: sinon.SinonSandbox; diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-query-history.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/remote-query-history.test.ts similarity index 93% rename from extensions/ql-vscode/src/vscode-tests/no-workspace/remote-query-history.test.ts rename to extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/remote-query-history.test.ts index a25541bdbbc..bdf5ac9bc0d 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-query-history.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/remote-query-history.test.ts @@ -4,18 +4,18 @@ import * as sinon from 'sinon'; import { expect } from 'chai'; import { CancellationToken, ExtensionContext, Uri, window, workspace } from 'vscode'; -import { QueryHistoryConfig } from '../../config'; -import { DatabaseManager } from '../../databases'; -import { tmpDir } from '../../helpers'; -import { QueryHistoryManager } from '../../query-history'; -import { QueryServerClient } from '../../queryserver-client'; -import { Credentials } from '../../authentication'; -import { AnalysesResultsManager } from '../../remote-queries/analyses-results-manager'; -import { RemoteQueryResult } from '../../remote-queries/shared/remote-query-result'; -import { DisposableBucket } from '../disposable-bucket'; -import { testDisposeHandler } from '../test-dispose-handler'; -import { walkDirectory } from '../../helpers'; -import { getErrorMessage } from '../../pure/helpers-pure'; +import { QueryHistoryConfig } from '../../../config'; +import { DatabaseManager } from '../../../databases'; +import { tmpDir } from '../../../helpers'; +import { QueryHistoryManager } from '../../../query-history'; +import { QueryServerClient } from '../../../queryserver-client'; +import { Credentials } from '../../../authentication'; +import { AnalysesResultsManager } from '../../../remote-queries/analyses-results-manager'; +import { RemoteQueryResult } from '../../../remote-queries/shared/remote-query-result'; +import { DisposableBucket } from '../../disposable-bucket'; +import { testDisposeHandler } from '../../test-dispose-handler'; +import { walkDirectory } from '../../../helpers'; +import { getErrorMessage } from '../../../pure/helpers-pure'; /** * Tests for remote queries and how they interact with the query history manager. @@ -23,7 +23,7 @@ import { getErrorMessage } from '../../pure/helpers-pure'; describe('Remote queries and query history manager', function() { - const EXTENSION_PATH = path.join(__dirname, '../../../'); + const EXTENSION_PATH = path.join(__dirname, '../../../../'); const STORAGE_DIR = Uri.file(path.join(tmpDir.name, 'remote-queries')).fsPath; const asyncNoop = async () => { /** noop */ }; @@ -353,7 +353,7 @@ describe('Remote queries and query history manager', function() { async function copyHistoryState() { fs.ensureDirSync(STORAGE_DIR); - fs.copySync(path.join(__dirname, 'data/remote-queries/'), path.join(tmpDir.name, 'remote-queries')); + fs.copySync(path.join(__dirname, '../data/remote-queries/'), path.join(tmpDir.name, 'remote-queries')); // also, replace the files with "PLACEHOLDER" so that they have the correct directory for await (const p of walkDirectory(STORAGE_DIR)) { From 68f9a606bbcbe468004e32fcc6c91b3db2e825e8 Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Mon, 4 Apr 2022 12:07:40 +0100 Subject: [PATCH 2/6] Remove leftover comment --- .../no-workspace/remote-queries/repository-selection.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/repository-selection.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/repository-selection.test.ts index 9b85396cccc..9fb1c8cf618 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/repository-selection.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/repository-selection.test.ts @@ -21,7 +21,6 @@ describe('repository-selection', function() { showInputBoxSpy = sandbox.stub(window, 'showInputBox'); getRemoteRepositoryListsSpy = sandbox.stub(); showAndLogErrorMessageSpy = sandbox.stub(); - // extensions/ql-vscode/src/remote-queries/repository-selection.ts mod = proxyquire('../../../remote-queries/repository-selection', { '../config': { getRemoteRepositoryLists: getRemoteRepositoryListsSpy From f4376d2c68ef5f62f70493c74c51fe66d6874b5b Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Mon, 4 Apr 2022 15:28:42 +0100 Subject: [PATCH 3/6] Add support for system defined repository lists --- .../remote-queries/repository-selection.ts | 142 +++++++++++++----- .../src/remote-queries/run-remote-query.ts | 34 +++-- .../repository-selection.test.ts | 45 +++++- 3 files changed, 161 insertions(+), 60 deletions(-) diff --git a/extensions/ql-vscode/src/remote-queries/repository-selection.ts b/extensions/ql-vscode/src/remote-queries/repository-selection.ts index 6ad1b643654..c3eae1a1e21 100644 --- a/extensions/ql-vscode/src/remote-queries/repository-selection.ts +++ b/extensions/ql-vscode/src/remote-queries/repository-selection.ts @@ -1,54 +1,116 @@ import { QuickPickItem, window } from 'vscode'; import { showAndLogErrorMessage } from '../helpers'; -import { getRemoteRepositoryLists } from '../config'; import { logger } from '../logging'; +import { getRemoteRepositoryLists } from '../config'; import { REPO_REGEX } from '../pure/helpers-pure'; +export interface RepositorySelection { + repositories?: string[]; + repositoryLists?: string[] +} + interface RepoListQuickPickItem extends QuickPickItem { - repoList: string[]; + repositories?: string[]; + repositoryList?: string; + useCustomRepository?: boolean; } /** - * Gets the repositories to run the query against. + * Gets the repositories or repository lists to run the query against. + * @returns The user selection. */ -export async function getRepositories(): Promise { - const repoLists = getRemoteRepositoryLists(); - if (repoLists && Object.keys(repoLists).length) { - const quickPickItems = Object.entries(repoLists).map(([key, value]) => ( - { - label: key, // the name of the repository list - repoList: value, // the actual array of repositories - } - )); - const quickpick = await window.showQuickPick( - quickPickItems, - { - placeHolder: 'Select a repository list. You can define repository lists in the `codeQL.variantAnalysis.repositoryLists` setting.', - ignoreFocusOut: true, - }); - if (quickpick?.repoList.length) { - void logger.log(`Selected repositories: ${quickpick.repoList.join(', ')}`); - return quickpick.repoList; - } else { - void showAndLogErrorMessage('No repositories selected.'); - return; +export async function getRepositorySelection(): Promise { + const quickPickItems = [ + createCustomRepoQuickPickItem(), + ...createSystemDefinedRepoListsQuickPickItems(), + ...createUserDefinedRepoListsQuickPickItems(), + ]; + + const options = { + placeHolder: 'Select a repository list. You can define repository lists in the `codeQL.variantAnalysis.repositoryLists` setting.', + ignoreFocusOut: true, + }; + + const quickpick = await window.showQuickPick( + quickPickItems, + options); + + if (quickpick?.repositories?.length) { + void logger.log(`Selected repositories: ${quickpick.repositories.join(', ')}`); + return { repositories: quickpick.repositories }; + } else if (quickpick?.repositoryList) { + void logger.log(`Selected repository list: ${quickpick.repositoryList}`); + return { repositoryLists: [quickpick.repositoryList] }; + } else if (quickpick?.useCustomRepository) { + const customRepo = await getCustomRepo(); + if (!customRepo || !REPO_REGEX.test(customRepo)) { + void showAndLogErrorMessage('Invalid repository format. Please enter a valid repository in the format / (e.g. github/codeql)'); + return {}; } + void logger.log(`Entered repository: ${customRepo}`); + return { repositories: [customRepo] }; } else { - void logger.log('No repository lists defined. Displaying text input box.'); - const remoteRepo = await window.showInputBox({ - title: 'Enter a GitHub repository in the format / (e.g. github/codeql)', - placeHolder: '/', - prompt: 'Tip: you can save frequently used repositories in the `codeQL.variantAnalysis.repositoryLists` setting', - ignoreFocusOut: true, - }); - if (!remoteRepo) { - void showAndLogErrorMessage('No repositories entered.'); - return; - } else if (!REPO_REGEX.test(remoteRepo)) { // Check if user entered invalid input - void showAndLogErrorMessage('Invalid repository format. Must be in the format / (e.g. github/codeql)'); - return; - } - void logger.log(`Entered repository: ${remoteRepo}`); - return [remoteRepo]; + void showAndLogErrorMessage('No repositories selected.'); + return {}; + } +} + +/** + * Checks if the selection is valid or not. + * @param repoSelection The selection to check. + * @returns A boolean flag indicating if the selection is valid or not. + */ +export function isInvalidSelection(repoSelection: RepositorySelection): boolean { + if (repoSelection.repositories === undefined && repoSelection.repositoryLists === undefined) { + return true; + } + if (repoSelection.repositories !== undefined && repoSelection.repositories.length === 0) { + return true; } + if (repoSelection.repositoryLists !== undefined && repoSelection.repositoryLists.length === 0) { + return true; + } + + return false; +} + +function createSystemDefinedRepoListsQuickPickItems(): RepoListQuickPickItem[] { + const topNs = [10, 100, 1000]; + + return topNs.map(n => ({ + label: '$(star) Top ' + n, + repositoryList: `top_${n}`, + alwaysShow: true + } as RepoListQuickPickItem)); +} + +function createUserDefinedRepoListsQuickPickItems(): RepoListQuickPickItem[] { + const repoLists = getRemoteRepositoryLists(); + if (!repoLists) { + return []; + } + + return Object.entries(repoLists).map(([key, value]) => ( + { + label: key, // the name of the repository list + repositories: value, // the actual array of repositories + } + )); +} + +function createCustomRepoQuickPickItem(): RepoListQuickPickItem { + return { + label: '$(edit) Enter a GitHub repository', + useCustomRepository: true, + alwaysShow: true, + }; +} + +async function getCustomRepo(): Promise { + return await window.showInputBox({ + title: 'Enter a GitHub repository in the format / (e.g. github/codeql)', + placeHolder: '/', + prompt: 'Tip: you can save frequently used repositories in the `codeQL.variantAnalysis.repositoryLists` setting', + ignoreFocusOut: true, + }); } diff --git a/extensions/ql-vscode/src/remote-queries/run-remote-query.ts b/extensions/ql-vscode/src/remote-queries/run-remote-query.ts index 9f648b71d56..8a389dc55b2 100644 --- a/extensions/ql-vscode/src/remote-queries/run-remote-query.ts +++ b/extensions/ql-vscode/src/remote-queries/run-remote-query.ts @@ -22,7 +22,7 @@ import { RemoteQuery } from './remote-query'; import { RemoteQuerySubmissionResult } from './remote-query-submission-result'; import { QueryMetadata } from '../pure/interface-types'; import { getErrorMessage, REPO_REGEX } from '../pure/helpers-pure'; -import { getRepositories } from './repository-selection'; +import { getRepositorySelection, isInvalidSelection, RepositorySelection } from './repository-selection'; export interface QlPack { name: string; @@ -189,8 +189,8 @@ export async function runRemoteQuery( message: 'Determining query target language' }); - const repositories = await getRepositories(); - if (!repositories || repositories.length === 0) { + const repoSelection = await getRepositorySelection(); + if (isInvalidSelection(repoSelection)) { throw new UserCancellationException('No repositories to query.'); } @@ -249,7 +249,7 @@ export async function runRemoteQuery( }); const actionBranch = getActionBranch(); - const workflowRunId = await runRemoteQueriesApiRequest(credentials, actionBranch, language, repositories, owner, repo, base64Pack, dryRun); + const workflowRunId = await runRemoteQueriesApiRequest(credentials, actionBranch, language, repoSelection, owner, repo, base64Pack, dryRun); const queryStartTime = Date.now(); const queryMetadata = await tryGetQueryMetadata(cliServer, queryFile); @@ -287,15 +287,30 @@ async function runRemoteQueriesApiRequest( credentials: Credentials, ref: string, language: string, - repositories: string[], + repoSelection: RepositorySelection, owner: string, repo: string, queryPackBase64: string, dryRun = false ): Promise { + const data = { + ref, + language, + repositories: repoSelection.repositories ?? undefined, + repository_lists: repoSelection.repositoryLists ?? undefined, + query_pack: queryPackBase64, + }; + if (dryRun) { void showAndLogInformationMessage('[DRY RUN] Would have sent request. See extension log for the payload.'); - void logger.log(JSON.stringify({ ref, language, repositories, owner, repo, queryPackBase64: queryPackBase64.substring(0, 100) + '... ' + queryPackBase64.length + ' bytes' })); + void logger.log(JSON.stringify({ + owner, + repo, + data: { + ...data, + queryPackBase64: queryPackBase64.substring(0, 100) + '... ' + queryPackBase64.length + ' bytes' + } + })); return; } @@ -306,12 +321,7 @@ async function runRemoteQueriesApiRequest( { owner, repo, - data: { - ref, - language, - repositories, - query_pack: queryPackBase64, - } + data } ); const workflowRunId = response.data.workflow_run_id; diff --git a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/repository-selection.test.ts b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/repository-selection.test.ts index 9fb1c8cf618..6772edd8fb0 100644 --- a/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/repository-selection.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/repository-selection.test.ts @@ -8,7 +8,7 @@ const proxyquire = pq.noPreserveCache(); describe('repository-selection', function() { - describe('getRepositories', () => { + describe('getRepositorySelection', () => { let sandbox: sinon.SinonSandbox; let quickPickSpy: sinon.SinonStub; let showInputBoxSpy: sinon.SinonStub; @@ -35,10 +35,10 @@ describe('repository-selection', function() { sandbox.restore(); }); - it('should run on a repo list that you chose from your pre-defined config', async () => { + it('should allow selection from repo lists from your pre-defined config', async () => { // fake return values quickPickSpy.resolves( - { repoList: ['foo/bar', 'foo/baz'] } + { repositories: ['foo/bar', 'foo/baz'] } ); getRemoteRepositoryListsSpy.returns( { @@ -48,14 +48,37 @@ describe('repository-selection', function() { ); // make the function call - const repoList = await mod.getRepositories(); + const repoSelection = await mod.getRepositorySelection(); // Check that the return value is correct - expect(repoList).to.deep.eq( + expect(repoSelection.repositoryLists).to.be.undefined; + expect(repoSelection.repositories).to.deep.eq( ['foo/bar', 'foo/baz'] ); }); + it('should allow selection from repo lists defined at the system level', async () => { + // fake return values + quickPickSpy.resolves( + { repositoryList: 'top_100' } + ); + getRemoteRepositoryListsSpy.returns( + { + 'list1': ['foo/bar', 'foo/baz'], + 'list2': [], + } + ); + + // make the function call + const repoSelection = await mod.getRepositorySelection(); + + // Check that the return value is correct + expect(repoSelection.repositories).to.be.undefined; + expect(repoSelection.repositoryLists).to.deep.eq( + ['top_100'] + ); + }); + // Test the regex in various "good" cases const goodRepos = [ 'owner/repo', @@ -65,14 +88,17 @@ describe('repository-selection', function() { goodRepos.forEach(repo => { it(`should run on a valid repo that you enter in the text box: ${repo}`, async () => { // fake return values + quickPickSpy.resolves( + { useCustomRepository: true } + ); getRemoteRepositoryListsSpy.returns({}); // no pre-defined repo lists showInputBoxSpy.resolves(repo); // make the function call - const repoList = await mod.getRepositories(); + const repoSelection = await mod.getRepositorySelection(); // Check that the return value is correct - expect(repoList).to.deep.equal( + expect(repoSelection.repositories).to.deep.equal( [repo] ); }); @@ -88,11 +114,14 @@ describe('repository-selection', function() { badRepos.forEach(repo => { it(`should show an error message if you enter an invalid repo in the text box: ${repo}`, async () => { // fake return values + quickPickSpy.resolves( + { useCustomRepository: true } + ); getRemoteRepositoryListsSpy.returns({}); // no pre-defined repo lists showInputBoxSpy.resolves(repo); // make the function call - await mod.getRepositories(); + await mod.getRepositorySelection(); // check that we get the right error message expect(showAndLogErrorMessageSpy.firstCall.args[0]).to.contain('Invalid repository format'); From 1392fa04981133e67b4770bf5c21501d076c4309 Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Mon, 4 Apr 2022 15:53:45 +0100 Subject: [PATCH 4/6] Fix test --- .../cli-integration/remote-queries/run-remote-query.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/run-remote-query.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/run-remote-query.test.ts index 590c3173382..ed227a541a9 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/run-remote-query.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/run-remote-query.test.ts @@ -52,7 +52,7 @@ describe('Remote queries', function() { progress = sandbox.spy(); // Should not have asked for a language showQuickPickSpy = sandbox.stub(window, 'showQuickPick') - .onFirstCall().resolves({ repoList: ['github/vscode-codeql'] } as unknown as QuickPickItem) + .onFirstCall().resolves({ repositories: ['github/vscode-codeql'] } as unknown as QuickPickItem) .onSecondCall().resolves('javascript' as unknown as QuickPickItem); // always run in the vscode-codeql repo From a15f0c6bff2624110867eab2aec4558670c714fd Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Tue, 5 Apr 2022 09:03:15 +0100 Subject: [PATCH 5/6] Invert logic to make it more readable --- .../src/remote-queries/repository-selection.ts | 10 +++++----- .../ql-vscode/src/remote-queries/run-remote-query.ts | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/extensions/ql-vscode/src/remote-queries/repository-selection.ts b/extensions/ql-vscode/src/remote-queries/repository-selection.ts index c3eae1a1e21..fc8ccf62997 100644 --- a/extensions/ql-vscode/src/remote-queries/repository-selection.ts +++ b/extensions/ql-vscode/src/remote-queries/repository-selection.ts @@ -60,18 +60,18 @@ export async function getRepositorySelection(): Promise { * @param repoSelection The selection to check. * @returns A boolean flag indicating if the selection is valid or not. */ -export function isInvalidSelection(repoSelection: RepositorySelection): boolean { +export function isValidSelection(repoSelection: RepositorySelection): boolean { if (repoSelection.repositories === undefined && repoSelection.repositoryLists === undefined) { - return true; + return false; } if (repoSelection.repositories !== undefined && repoSelection.repositories.length === 0) { - return true; + return false; } if (repoSelection.repositoryLists !== undefined && repoSelection.repositoryLists.length === 0) { - return true; + return false; } - return false; + return true; } function createSystemDefinedRepoListsQuickPickItems(): RepoListQuickPickItem[] { diff --git a/extensions/ql-vscode/src/remote-queries/run-remote-query.ts b/extensions/ql-vscode/src/remote-queries/run-remote-query.ts index 8a389dc55b2..cfb99ef3f51 100644 --- a/extensions/ql-vscode/src/remote-queries/run-remote-query.ts +++ b/extensions/ql-vscode/src/remote-queries/run-remote-query.ts @@ -22,7 +22,7 @@ import { RemoteQuery } from './remote-query'; import { RemoteQuerySubmissionResult } from './remote-query-submission-result'; import { QueryMetadata } from '../pure/interface-types'; import { getErrorMessage, REPO_REGEX } from '../pure/helpers-pure'; -import { getRepositorySelection, isInvalidSelection, RepositorySelection } from './repository-selection'; +import { getRepositorySelection, isValidSelection, RepositorySelection } from './repository-selection'; export interface QlPack { name: string; @@ -190,7 +190,7 @@ export async function runRemoteQuery( }); const repoSelection = await getRepositorySelection(); - if (isInvalidSelection(repoSelection)) { + if (!isValidSelection(repoSelection)) { throw new UserCancellationException('No repositories to query.'); } From f67df09b48849a0193225e70cbfc24d4e95d1259 Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Wed, 6 Apr 2022 08:34:09 +0100 Subject: [PATCH 6/6] Apply suggestions from code review Co-authored-by: Andrew Eisenberg --- .../ql-vscode/src/remote-queries/repository-selection.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/ql-vscode/src/remote-queries/repository-selection.ts b/extensions/ql-vscode/src/remote-queries/repository-selection.ts index fc8ccf62997..27fa8dab19b 100644 --- a/extensions/ql-vscode/src/remote-queries/repository-selection.ts +++ b/extensions/ql-vscode/src/remote-queries/repository-selection.ts @@ -67,7 +67,7 @@ export function isValidSelection(repoSelection: RepositorySelection): boolean { if (repoSelection.repositories !== undefined && repoSelection.repositories.length === 0) { return false; } - if (repoSelection.repositoryLists !== undefined && repoSelection.repositoryLists.length === 0) { + if (repoSelection.repositoryLists?.length === 0) { return false; } @@ -90,10 +90,10 @@ function createUserDefinedRepoListsQuickPickItems(): RepoListQuickPickItem[] { return []; } - return Object.entries(repoLists).map(([key, value]) => ( + return Object.entries(repoLists).map(([label, repositories]) => ( { - label: key, // the name of the repository list - repositories: value, // the actual array of repositories + label, // the name of the repository list + repositories // the actual array of repositories } )); }