@@ -2,18 +2,20 @@ import * as fs from "fs";
22import * as path from "path" ;
33import { performance } from "perf_hooks" ;
44
5+ import * as core from "@actions/core" ;
56import * as toolrunner from "@actions/exec/lib/toolrunner" ;
67import del from "del" ;
78import * as yaml from "js-yaml" ;
89
9- import { DatabaseCreationTimings } from "./actions-util" ;
10+ import { DatabaseCreationTimings , EventReport } from "./actions-util" ;
1011import * as analysisPaths from "./analysis-paths" ;
1112import { CodeQL , getCodeQL } from "./codeql" ;
1213import * as configUtils from "./config-utils" ;
1314import { FeatureEnablement , Feature } from "./feature-flags" ;
1415import { isScannedLanguage , Language } from "./languages" ;
1516import { Logger } from "./logging" ;
1617import { endTracingForCluster } from "./tracer-config" ;
18+ import { validateSarifFileSchema } from "./upload-lib" ;
1719import * as util from "./util" ;
1820
1921export class CodeQLAnalysisError extends Error {
@@ -78,6 +80,8 @@ export interface QueriesStatusReport {
7880 interpret_results_swift_duration_ms ?: number ;
7981 /** Name of language that errored during analysis (or undefined if no language failed). */
8082 analyze_failure_language ?: string ;
83+ /** Reports on discrete events associated with this status report. */
84+ event_reports ?: EventReport [ ] ;
8185}
8286
8387async function setupPythonExtractor (
@@ -242,6 +246,9 @@ export async function runQueries(
242246 const packsWithVersion = config . packs [ language ] || [ ] ;
243247
244248 try {
249+ const sarifFile = path . join ( sarifFolder , `${ language } .sarif` ) ;
250+ let startTimeInterpretResults : number ;
251+ let endTimeInterpretResults : number ;
245252 if ( await util . useCodeScanningConfigInCli ( codeql , features ) ) {
246253 // If we are using the code scanning config in the CLI,
247254 // much of the work needed to generate the query suites
@@ -257,16 +264,16 @@ export async function runQueries(
257264 new Date ( ) . getTime ( ) - startTimeBuiltIn ;
258265
259266 logger . startGroup ( `Interpreting results for ${ language } ` ) ;
260- const startTimeInterpretResults = new Date ( ) . getTime ( ) ;
261- const sarifFile = path . join ( sarifFolder , `${ language } .sarif` ) ;
267+ startTimeInterpretResults = new Date ( ) . getTime ( ) ;
262268 const analysisSummary = await runInterpretResults (
263269 language ,
264270 undefined ,
265271 sarifFile ,
266272 config . debugMode
267273 ) ;
274+ endTimeInterpretResults = new Date ( ) . getTime ( ) ;
268275 statusReport [ `interpret_results_${ language } _duration_ms` ] =
269- new Date ( ) . getTime ( ) - startTimeInterpretResults ;
276+ endTimeInterpretResults - startTimeInterpretResults ;
270277 logger . endGroup ( ) ;
271278 logger . info ( analysisSummary ) ;
272279 } else {
@@ -342,19 +349,43 @@ export async function runQueries(
342349 }
343350 logger . endGroup ( ) ;
344351 logger . startGroup ( `Interpreting results for ${ language } ` ) ;
345- const startTimeInterpretResults = new Date ( ) . getTime ( ) ;
346- const sarifFile = path . join ( sarifFolder , `${ language } .sarif` ) ;
352+ startTimeInterpretResults = new Date ( ) . getTime ( ) ;
347353 const analysisSummary = await runInterpretResults (
348354 language ,
349355 querySuitePaths ,
350356 sarifFile ,
351357 config . debugMode
352358 ) ;
359+ endTimeInterpretResults = new Date ( ) . getTime ( ) ;
353360 statusReport [ `interpret_results_${ language } _duration_ms` ] =
354- new Date ( ) . getTime ( ) - startTimeInterpretResults ;
361+ endTimeInterpretResults - startTimeInterpretResults ;
355362 logger . endGroup ( ) ;
356363 logger . info ( analysisSummary ) ;
357364 }
365+ if ( await features . getValue ( Feature . QaTelemetryEnabled ) ) {
366+ const perQueryAlertCounts = getPerQueryAlertCounts ( sarifFile , logger ) ;
367+
368+ const perQueryAlertCountEventReport : EventReport = {
369+ event : "codeql database interpret-results" ,
370+ started_at : startTimeInterpretResults . toString ( ) ,
371+ completed_at : endTimeInterpretResults . toString ( ) ,
372+ exit_status : "success" ,
373+ language,
374+ properties : perQueryAlertCounts ,
375+ } ;
376+
377+ core . info (
378+ `New event report:\n\n${ JSON . stringify (
379+ perQueryAlertCountEventReport
380+ ) } `
381+ ) ;
382+
383+ if ( statusReport [ "event_reports" ] === undefined ) {
384+ statusReport [ "event_reports" ] = [ ] ;
385+ }
386+ statusReport [ "event_reports" ] . push ( perQueryAlertCountEventReport ) ;
387+ }
388+
358389 await runPrintLinesOfCode ( language ) ;
359390 } catch ( e ) {
360391 logger . info ( String ( e ) ) ;
@@ -392,6 +423,38 @@ export async function runQueries(
392423 ) ;
393424 }
394425
426+ /** Get an object with all queries and their counts parsed from a SARIF file path. */
427+ function getPerQueryAlertCounts (
428+ sarifPath : string ,
429+ log : Logger
430+ ) : Record < string , number > {
431+ validateSarifFileSchema ( sarifPath , log ) ;
432+ const sarifObject = JSON . parse (
433+ fs . readFileSync ( sarifPath , "utf8" )
434+ ) as util . SarifFile ;
435+ // We do not need to compute fingerprints because we are not sending data based off of locations.
436+
437+ // Generate the query: alert count object
438+ const perQueryAlertCounts : Record < string , number > = { } ;
439+
440+ // All rules (queries), from all results, from all runs
441+ for ( const sarifRun of sarifObject . runs ) {
442+ if ( sarifRun . results ) {
443+ for ( const result of sarifRun . results ) {
444+ if ( result . ruleId ) {
445+ if ( result . ruleId in perQueryAlertCounts ) {
446+ perQueryAlertCounts [ result . ruleId ] =
447+ perQueryAlertCounts [ result . ruleId ] + 1 ;
448+ } else {
449+ perQueryAlertCounts [ result . ruleId ] = 1 ;
450+ }
451+ }
452+ }
453+ }
454+ }
455+ return perQueryAlertCounts ;
456+ }
457+
395458 async function runPrintLinesOfCode ( language : Language ) : Promise < string > {
396459 const databasePath = util . getCodeQLDatabasePath ( config , language ) ;
397460 return await codeql . databasePrintBaseline ( databasePath ) ;
0 commit comments