diff --git a/sdk/typescript/src/bulk-scan-discovery.ts b/sdk/typescript/src/bulk-scan-discovery.ts index 3558bccf6..58bdb1472 100644 --- a/sdk/typescript/src/bulk-scan-discovery.ts +++ b/sdk/typescript/src/bulk-scan-discovery.ts @@ -147,7 +147,11 @@ export async function runBulkScanWizard( } prompt.write(`\nFound ${discovered.length} repositories.\n`); - const repositories = await selectGitHubRepositories(discovered, prompt); + const repositories = await selectGitHubRepositories( + discovered, + prompt, + signal, + ); const outputDir = resolve( dependencies.currentDirectory(), @@ -155,6 +159,7 @@ export async function runBulkScanWizard( await prompt.input( "Where should scan results be saved?", defaultOutputDir, + signal, ), ), ); @@ -164,7 +169,7 @@ export async function runBulkScanWizard( `\nReady to scan ${repositories.length} repositories?\n` + `Results: ${outputDir}\nRepository list: ${inputPath}\n`, ); - if (!(await prompt.confirm("Start scanning?"))) { + if (!(await prompt.confirm("Start scanning?", false, signal))) { prompt.write("\nScan canceled.\n"); return null; } @@ -212,6 +217,8 @@ async function selectGitHubOwner( return await prompt.select( "Which account or organization should we scan?", owners.map((owner) => ({ label: owner, value: owner })), + undefined, + signal, ); } prompt.write(`\nFinding repositories in ${personal}.\n`); @@ -221,6 +228,7 @@ async function selectGitHubOwner( async function selectGitHubRepositories( repositories: GitHubRepository[], prompt: BulkScanPrompt, + signal?: AbortSignal, ): Promise { const selected = new Set(); while (selected.size < repositories.length) { @@ -237,6 +245,8 @@ async function selectGitHubRepositories( .filter(({ fullName }) => !selected.has(fullName)) .map(({ fullName }) => ({ label: fullName, value: fullName })), ], + undefined, + signal, ); if (!choice) break; selected.add(choice); diff --git a/sdk/typescript/tests-ts/bulk-scan-discovery.test.ts b/sdk/typescript/tests-ts/bulk-scan-discovery.test.ts index cf4e1b26a..da6c31f9d 100644 --- a/sdk/typescript/tests-ts/bulk-scan-discovery.test.ts +++ b/sdk/typescript/tests-ts/bulk-scan-discovery.test.ts @@ -37,6 +37,7 @@ async function temporaryDirectory(): Promise { class FakePrompt implements BulkScanPrompt { public readonly messages: string[] = []; public readonly questions: string[] = []; + public readonly signals: (AbortSignal | undefined)[] = []; public interactive = true; public confirms: boolean[] = []; public inputs: string[] = []; @@ -51,21 +52,34 @@ class FakePrompt implements BulkScanPrompt { this.messages.push(value); } - public async confirm(question: string, fallback = false): Promise { + public async confirm( + question: string, + fallback = false, + signal?: AbortSignal, + ): Promise { this.questions.push(question); + this.signals.push(signal); return this.confirms.shift() ?? fallback; } - public async input(question: string, fallback = ""): Promise { + public async input( + question: string, + fallback = "", + signal?: AbortSignal, + ): Promise { this.questions.push(question); + this.signals.push(signal); return this.inputs.shift() ?? fallback; } public async select( question: string, options: readonly { label: string; value: Value }[], + _presentation?: unknown, + signal?: AbortSignal, ): Promise { this.questions.push(question); + this.signals.push(signal); this.searchOptions.push(options.map(({ label }) => label)); const value = this.choices.shift(); return (options.find((option) => option.value === value) ?? options[0]!) @@ -461,4 +475,16 @@ describe("bulk scan repository discovery", () => { expect(prompt.questions).toEqual([]); expect(requests).toEqual([]); }); + + test("passes cancellation to every setup prompt", async () => { + const root = await temporaryDirectory(); + const { dependencies, prompt } = discoveryDependencies(root, { + organizations: ["acme"], + }); + const signal = new AbortController().signal; + + await runBulkScanWizard(dependencies, signal); + + expect(prompt.signals).toEqual([signal, signal, signal, signal]); + }); });