( cliArgs: string[], oidcToken: string | undefined, )
| 315 | } |
| 316 | |
| 317 | async function runPromptfooScan( |
| 318 | cliArgs: string[], |
| 319 | oidcToken: string | undefined, |
| 320 | ): Promise<ScanResponse> { |
| 321 | const installEnv = createSubprocessEnv(); |
| 322 | |
| 323 | core.info('📦 Installing promptfoo...'); |
| 324 | await exec.exec('npm', ['install', '-g', 'promptfoo'], { env: installEnv }); |
| 325 | core.info('✅ Promptfoo installed successfully'); |
| 326 | |
| 327 | core.info('🚀 Running promptfoo code-scans run...'); |
| 328 | |
| 329 | let scanOutput = ''; |
| 330 | let scanError = ''; |
| 331 | const scanEnv = createScanEnv(oidcToken); |
| 332 | |
| 333 | const exitCode = await exec.exec('promptfoo', cliArgs, { |
| 334 | env: scanEnv, |
| 335 | listeners: { |
| 336 | stdout: (data: Buffer) => { |
| 337 | scanOutput += data.toString(); |
| 338 | }, |
| 339 | stderr: (data: Buffer) => { |
| 340 | scanError += data.toString(); |
| 341 | }, |
| 342 | }, |
| 343 | ignoreReturnCode: true, |
| 344 | }); |
| 345 | |
| 346 | if (exitCode === 0) { |
| 347 | core.info('✅ Scan completed successfully'); |
| 348 | return parseScanOutput(scanOutput); |
| 349 | } |
| 350 | |
| 351 | // Keep compatibility with CLI releases that reported an authorized fork skip as text |
| 352 | // while the action and CLI roll out independently. |
| 353 | if (`${scanOutput}\n${scanError}`.includes('Fork PR scanning not authorized')) { |
| 354 | return { |
| 355 | success: true, |
| 356 | comments: [], |
| 357 | skipReason: FORK_PR_AUTH_SKIP_REASON, |
| 358 | }; |
| 359 | } |
| 360 | |
| 361 | core.error(`CLI exited with code ${exitCode}`); |
| 362 | core.error(`Error output: ${scanError}`); |
| 363 | throw new Error(`Code scan failed with exit code ${exitCode}`); |
| 364 | } |
| 365 | |
| 366 | function getScanResponse(cliArgs: string[], oidcToken: string | undefined): Promise<ScanResponse> { |
| 367 | if (process.env.ACT === 'true') { |
no test coverage detected
searching dependent graphs…