diff --git a/sdk/typescript/src/multiscan.ts b/sdk/typescript/src/multiscan.ts index c63019d2e..dfb493c91 100644 --- a/sdk/typescript/src/multiscan.ts +++ b/sdk/typescript/src/multiscan.ts @@ -56,6 +56,7 @@ interface MultiscanReceipt extends MultiscanTask { cost?: ScanCost; error?: string; warning?: string; + warnings?: string[]; } export interface MultiscanOptions { @@ -166,6 +167,16 @@ async function runCampaign( receipt.outputDir === selectedArtifactOutput) && (await hasArtifacts(artifactOutput)) ) { + if (receipt.status !== "failed") { + for (const warning of receipt.warnings ?? []) { + notifyProgress(options, { + repository: task.id, + status: receipt.status, + attempt: receipt.attempt, + warning, + }); + } + } if (receipt.status === "completed") { completed += 1; continue; @@ -228,6 +239,7 @@ async function runCampaign( notifyProgress(options, { ...progress, status: "started" }); let failure: string | undefined; let warning: string | undefined; + const runWarnings: string[] = []; let coverage: CoverageDocument["completeness"] | undefined; let cost: Readonly | null = null; let exhaustedBudget = false; @@ -272,12 +284,14 @@ async function runCampaign( ...(options.maxCostUsd === undefined ? {} : { maxCostUsd: options.maxCostUsd }), - onWarning: (warning) => + onWarning: (warning) => { + runWarnings.push(warning); notifyProgress(options, { ...progress, status: "started", warning, - }), + }); + }, ...(options.signal === undefined ? {} : { signal: options.signal }), }); cost = result.cost; @@ -317,6 +331,7 @@ async function runCampaign( ...(cost === null ? {} : { cost }), ...(failure === undefined ? {} : { error: failure }), ...(warning === undefined ? {} : { warning }), + ...(runWarnings.length === 0 ? {} : { warnings: runWarnings }), })}\n`, ); notifyProgress(options, { diff --git a/sdk/typescript/tests-ts/multiscan.test.ts b/sdk/typescript/tests-ts/multiscan.test.ts index c1c9e9ddd..ad4226ae7 100644 --- a/sdk/typescript/tests-ts/multiscan.test.ts +++ b/sdk/typescript/tests-ts/multiscan.test.ts @@ -369,6 +369,7 @@ describe("multiscan", () => { paths, client(async (_repository, scanOptions = {}) => { scanOptions.onWarning?.("Could not run post-scan instructions."); + scanOptions.onWarning?.("Repository changed during the scan."); return await completedScan(scanOptions.outputDir!); }), { onProgress: (event) => progress.push(event) }, @@ -382,6 +383,45 @@ describe("multiscan", () => { status: "started", warning: "Could not run post-scan instructions.", }); + expect(await results(summary.resultsPath)).toMatchObject([ + { + id: "follow-up-warning", + status: "completed", + warnings: [ + "Could not run post-scan instructions.", + "Repository changed during the scan.", + ], + }, + ]); + + const resumedProgress: typeof progress = []; + const resumed = await runMultiscan( + options( + paths, + client(async () => Promise.reject(new Error("must not rerun"))), + { + onProgress: (event) => resumedProgress.push(event), + }, + ), + ); + + expect(resumed).toMatchObject({ completed: 1, skipped: 1, failed: 0 }); + expect(resumedProgress).toEqual( + expect.arrayContaining([ + { + repository: "follow-up-warning", + attempt: 1, + status: "completed", + warning: "Could not run post-scan instructions.", + }, + { + repository: "follow-up-warning", + attempt: 1, + status: "completed", + warning: "Repository changed during the scan.", + }, + ]), + ); }); test.each([false, true])(