Skip to content

Commit 741a910

Browse files
committed
Send per-query alert count event reports under QA telemetry flag
1 parent cff3d9e commit 741a910

8 files changed

Lines changed: 157 additions & 16 deletions

File tree

lib/actions-util.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/analyze.js

Lines changed: 52 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/analyze.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/feature-flags.js

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/feature-flags.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/actions-util.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,25 @@ export type ActionStatus =
315315
| "failure"
316316
| "user-error";
317317

318+
// Any status report may include an array of EventReports associated with it.
319+
export interface EventReport {
320+
/** An enumerable description of the event. */
321+
event: string;
322+
/** Time this event started. */
323+
started_at: string;
324+
/** Time this event ended. */
325+
completed_at: string;
326+
/** eg: `success`, `failure`, `timeout`, etc. */
327+
exit_status?: string;
328+
/** If the event is language-specific. */
329+
language?: string;
330+
/**
331+
* A generic JSON blob of data related to this event.
332+
* Use Object.assign() to append additional fields to the object.
333+
*/
334+
properties?: object;
335+
}
336+
318337
export interface StatusReportBase {
319338
/**
320339
* UUID representing the job run that this status report belongs to. We

src/analyze.ts

Lines changed: 70 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,20 @@ import * as fs from "fs";
22
import * as path from "path";
33
import { performance } from "perf_hooks";
44

5+
import * as core from "@actions/core";
56
import * as toolrunner from "@actions/exec/lib/toolrunner";
67
import del from "del";
78
import * as yaml from "js-yaml";
89

9-
import { DatabaseCreationTimings } from "./actions-util";
10+
import { DatabaseCreationTimings, EventReport } from "./actions-util";
1011
import * as analysisPaths from "./analysis-paths";
1112
import { CodeQL, getCodeQL } from "./codeql";
1213
import * as configUtils from "./config-utils";
1314
import { FeatureEnablement, Feature } from "./feature-flags";
1415
import { isScannedLanguage, Language } from "./languages";
1516
import { Logger } from "./logging";
1617
import { endTracingForCluster } from "./tracer-config";
18+
import { validateSarifFileSchema } from "./upload-lib";
1719
import * as util from "./util";
1820

1921
export 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

8387
async 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);

src/feature-flags.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as fs from "fs";
22
import * as path from "path";
33

4+
import * as core from "@actions/core";
45
import * as semver from "semver";
56

67
import { getApiClient } from "./api-client";
@@ -43,6 +44,7 @@ export enum Feature {
4344
ExportCodeScanningConfigEnabled = "export_code_scanning_config_enabled",
4445
ExportDiagnosticsEnabled = "export_diagnostics_enabled",
4546
MlPoweredQueriesEnabled = "ml_powered_queries_enabled",
47+
QaTelemetryEnabled = "qa_telemetry_enabled",
4648
UploadFailedSarifEnabled = "upload_failed_sarif_enabled",
4749
}
4850

@@ -76,6 +78,11 @@ export const featureConfig: Record<
7678
minimumVersion: "2.7.5",
7779
defaultValue: false,
7880
},
81+
[Feature.QaTelemetryEnabled]: {
82+
envVar: "CODEQL_ACTION_QA_TELEMETRY",
83+
minimumVersion: undefined,
84+
defaultValue: false,
85+
},
7986
[Feature.UploadFailedSarifEnabled]: {
8087
envVar: "CODEQL_ACTION_UPLOAD_FAILED_SARIF",
8188
minimumVersion: "2.11.3",

0 commit comments

Comments
 (0)