Gracefully continue if createStatusReportBase throws (#2225)

Previously, we weren't catching any possible exceptions in `createStatusReportBase` and runs would fail if any of the telemetry sub-items threw exceptions. As telemetry should not block the analysis, we continue here even if the status report throws.
This commit is contained in:
Angela P Wen 2024-04-04 15:26:14 -07:00 committed by GitHub
parent f421cda8e7
commit 7df281f2fe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 505 additions and 418 deletions

View file

@ -74,22 +74,24 @@ async function sendStatusReport(
error?.message,
error?.stack,
);
const report: FinishStatusReport = {
...statusReportBase,
...(stats || {}),
...(dbCreationTimings || {}),
};
if (config && didUploadTrapCaches) {
const trapCacheUploadStatusReport: FinishWithTrapUploadStatusReport = {
...report,
trap_cache_upload_duration_ms: Math.round(trapCacheUploadTime || 0),
trap_cache_upload_size_bytes: Math.round(
await getTotalCacheSize(config.trapCaches, logger),
),
if (statusReportBase !== undefined) {
const report: FinishStatusReport = {
...statusReportBase,
...(stats || {}),
...(dbCreationTimings || {}),
};
await statusReport.sendStatusReport(trapCacheUploadStatusReport);
} else {
await statusReport.sendStatusReport(report);
if (config && didUploadTrapCaches) {
const trapCacheUploadStatusReport: FinishWithTrapUploadStatusReport = {
...report,
trap_cache_upload_duration_ms: Math.round(trapCacheUploadTime || 0),
trap_cache_upload_size_bytes: Math.round(
await getTotalCacheSize(config.trapCaches, logger),
),
};
await statusReport.sendStatusReport(trapCacheUploadStatusReport);
} else {
await statusReport.sendStatusReport(report);
}
}
}
@ -190,16 +192,17 @@ async function run() {
const logger = getActionsLogger();
try {
await statusReport.sendStatusReport(
await createStatusReportBase(
ActionName.Analyze,
"starting",
startedAt,
config,
await util.checkDiskUsage(logger),
logger,
),
const statusReportBase = await createStatusReportBase(
ActionName.Analyze,
"starting",
startedAt,
config,
await util.checkDiskUsage(logger),
logger,
);
if (statusReportBase !== undefined) {
await statusReport.sendStatusReport(statusReportBase);
}
config = await getConfig(actionsUtil.getTemporaryDirectory(), logger);
if (config === undefined) {

View file

@ -55,12 +55,14 @@ async function sendCompletedStatusReport(
cause?.message,
cause?.stack,
);
const statusReport: AutobuildStatusReport = {
...statusReportBase,
autobuild_languages: allLanguages.join(","),
autobuild_failure: failingLanguage,
};
await sendStatusReport(statusReport);
if (statusReportBase !== undefined) {
const statusReport: AutobuildStatusReport = {
...statusReportBase,
autobuild_languages: allLanguages.join(","),
autobuild_failure: failingLanguage,
};
await sendStatusReport(statusReport);
}
}
async function run() {
@ -70,16 +72,17 @@ async function run() {
let currentLanguage: Language | undefined;
let languages: Language[] | undefined;
try {
await sendStatusReport(
await createStatusReportBase(
ActionName.Autobuild,
"starting",
startedAt,
config,
await checkDiskUsage(logger),
logger,
),
const statusReportBase = await createStatusReportBase(
ActionName.Autobuild,
"starting",
startedAt,
config,
await checkDiskUsage(logger),
logger,
);
if (statusReportBase !== undefined) {
await sendStatusReport(statusReportBase);
}
const gitHubVersion = await getGitHubVersion();
checkGitHubVersionInRange(gitHubVersion, logger);

View file

@ -76,18 +76,19 @@ async function runWrapper() {
const error = wrapError(unwrappedError);
core.setFailed(error.message);
await sendStatusReport(
await createStatusReportBase(
ActionName.InitPost,
getActionsStatus(error),
startedAt,
config,
await checkDiskUsage(),
logger,
error.message,
error.stack,
),
const statusReportBase = await createStatusReportBase(
ActionName.InitPost,
getActionsStatus(error),
startedAt,
config,
await checkDiskUsage(),
logger,
error.message,
error.stack,
);
if (statusReportBase !== undefined) {
await sendStatusReport(statusReportBase);
}
return;
}
const jobStatus = initActionPostHelper.getFinalJobStatus();
@ -101,12 +102,14 @@ async function runWrapper() {
await checkDiskUsage(),
logger,
);
const statusReport: InitPostStatusReport = {
...statusReportBase,
...uploadFailedSarifResult,
job_status: initActionPostHelper.getFinalJobStatus(),
};
await sendStatusReport(statusReport);
if (statusReportBase !== undefined) {
const statusReport: InitPostStatusReport = {
...statusReportBase,
...uploadFailedSarifResult,
job_status: initActionPostHelper.getFinalJobStatus(),
};
await sendStatusReport(statusReport);
}
}
void runWrapper();

View file

@ -120,6 +120,10 @@ async function sendCompletedStatusReport(
error?.stack,
);
if (statusReportBase === undefined) {
return;
}
const workflowLanguages = getOptionalInput("languages");
const initStatusReport: InitStatusReport = {
@ -226,17 +230,17 @@ async function run() {
core.exportVariable(EnvVar.INIT_ACTION_HAS_RUN, "true");
try {
await sendStatusReport(
await createStatusReportBase(
ActionName.Init,
"starting",
startedAt,
config,
await checkDiskUsage(logger),
logger,
),
const statusReportBase = await createStatusReportBase(
ActionName.Init,
"starting",
startedAt,
config,
await checkDiskUsage(logger),
logger,
);
if (statusReportBase !== undefined) {
await sendStatusReport(statusReportBase);
}
const codeQLDefaultVersionInfo = await features.getDefaultCliVersion(
gitHubVersion.type,
);
@ -315,18 +319,19 @@ async function run() {
} catch (unwrappedError) {
const error = wrapError(unwrappedError);
core.setFailed(error.message);
await sendStatusReport(
await createStatusReportBase(
ActionName.Init,
error instanceof ConfigurationError ? "user-error" : "aborted",
startedAt,
config,
await checkDiskUsage(),
logger,
error.message,
error.stack,
),
const statusReportBase = await createStatusReportBase(
ActionName.Init,
error instanceof ConfigurationError ? "user-error" : "aborted",
startedAt,
config,
await checkDiskUsage(),
logger,
error.message,
error.stack,
);
if (statusReportBase !== undefined) {
await sendStatusReport(statusReportBase);
}
return;
}

View file

@ -34,16 +34,17 @@ async function run() {
let config: Config | undefined;
try {
await sendStatusReport(
await createStatusReportBase(
ActionName.ResolveEnvironment,
"starting",
startedAt,
config,
await checkDiskUsage(),
logger,
),
const statusReportBase = await createStatusReportBase(
ActionName.ResolveEnvironment,
"starting",
startedAt,
config,
await checkDiskUsage(),
logger,
);
if (statusReportBase !== undefined) {
await sendStatusReport(statusReportBase);
}
const gitHubVersion = await getGitHubVersion();
checkGitHubVersionInRange(gitHubVersion, logger);
@ -80,33 +81,35 @@ async function run() {
`Failed to resolve a build environment suitable for automatically building your code. ${error.message}`,
);
await sendStatusReport(
await createStatusReportBase(
ActionName.ResolveEnvironment,
getActionsStatus(error),
startedAt,
config,
await checkDiskUsage(),
logger,
error.message,
error.stack,
),
const statusReportBase = await createStatusReportBase(
ActionName.ResolveEnvironment,
getActionsStatus(error),
startedAt,
config,
await checkDiskUsage(),
logger,
error.message,
error.stack,
);
if (statusReportBase !== undefined) {
await sendStatusReport(statusReportBase);
}
}
return;
}
await sendStatusReport(
await createStatusReportBase(
ActionName.ResolveEnvironment,
"success",
startedAt,
config,
await checkDiskUsage(),
logger,
),
const statusReportBase = await createStatusReportBase(
ActionName.ResolveEnvironment,
"success",
startedAt,
config,
await checkDiskUsage(),
logger,
);
if (statusReportBase !== undefined) {
await sendStatusReport(statusReportBase);
}
}
async function runWrapper() {

View file

@ -51,32 +51,35 @@ test("createStatusReportBase", async (t) => {
"failure cause",
"exception stack trace",
);
t.truthy(statusReport);
t.is(statusReport.action_name, ActionName.Init);
t.is(statusReport.action_oid, "unknown");
t.is(typeof statusReport.action_version, "string");
t.is(
statusReport.action_started_at,
new Date("May 19, 2023 05:19:00").toISOString(),
);
t.is(statusReport.actions_event_name, "dynamic");
t.is(statusReport.analysis_key, "analysis-key");
t.is(statusReport.build_mode, BuildMode.None);
t.is(statusReport.cause, "failure cause");
t.is(statusReport.commit_oid, process.env["GITHUB_SHA"]!);
t.is(statusReport.exception, "exception stack trace");
t.is(statusReport.job_name, process.env["GITHUB_JOB"] || "");
t.is(typeof statusReport.job_run_uuid, "string");
t.is(statusReport.languages, "java,swift");
t.is(statusReport.ref, process.env["GITHUB_REF"]!);
t.is(statusReport.runner_available_disk_space_bytes, 100);
t.is(statusReport.runner_image_version, process.env["ImageVersion"]);
t.is(statusReport.runner_os, process.env["RUNNER_OS"]!);
t.is(statusReport.started_at, process.env[EnvVar.WORKFLOW_STARTED_AT]!);
t.is(statusReport.status, "failure");
t.is(statusReport.workflow_name, process.env["GITHUB_WORKFLOW"] || "");
t.is(statusReport.workflow_run_attempt, 2);
t.is(statusReport.workflow_run_id, 100);
if (statusReport !== undefined) {
t.is(statusReport.action_name, ActionName.Init);
t.is(statusReport.action_oid, "unknown");
t.is(typeof statusReport.action_version, "string");
t.is(
statusReport.action_started_at,
new Date("May 19, 2023 05:19:00").toISOString(),
);
t.is(statusReport.actions_event_name, "dynamic");
t.is(statusReport.analysis_key, "analysis-key");
t.is(statusReport.build_mode, BuildMode.None);
t.is(statusReport.cause, "failure cause");
t.is(statusReport.commit_oid, process.env["GITHUB_SHA"]!);
t.is(statusReport.exception, "exception stack trace");
t.is(statusReport.job_name, process.env["GITHUB_JOB"] || "");
t.is(typeof statusReport.job_run_uuid, "string");
t.is(statusReport.languages, "java,swift");
t.is(statusReport.ref, process.env["GITHUB_REF"]!);
t.is(statusReport.runner_available_disk_space_bytes, 100);
t.is(statusReport.runner_image_version, process.env["ImageVersion"]);
t.is(statusReport.runner_os, process.env["RUNNER_OS"]!);
t.is(statusReport.started_at, process.env[EnvVar.WORKFLOW_STARTED_AT]!);
t.is(statusReport.status, "failure");
t.is(statusReport.workflow_name, process.env["GITHUB_WORKFLOW"] || "");
t.is(statusReport.workflow_run_attempt, 2);
t.is(statusReport.workflow_run_id, 100);
}
});
});
@ -96,7 +99,7 @@ test("createStatusReportBase_firstParty", async (t) => {
"failure cause",
"exception stack trace",
)
).first_party_analysis,
)?.first_party_analysis,
false,
);
@ -112,7 +115,7 @@ test("createStatusReportBase_firstParty", async (t) => {
"failure cause",
"exception stack trace",
)
).first_party_analysis,
)?.first_party_analysis,
true,
);
@ -129,7 +132,7 @@ test("createStatusReportBase_firstParty", async (t) => {
"failure cause",
"exception stack trace",
)
).first_party_analysis,
)?.first_party_analysis,
false,
);
@ -145,7 +148,7 @@ test("createStatusReportBase_firstParty", async (t) => {
"failure cause",
"exception stack trace",
)
).first_party_analysis,
)?.first_party_analysis,
true,
);
@ -162,7 +165,7 @@ test("createStatusReportBase_firstParty", async (t) => {
"failure cause",
"exception stack trace",
)
).first_party_analysis,
)?.first_party_analysis,
true,
);
@ -178,7 +181,7 @@ test("createStatusReportBase_firstParty", async (t) => {
"failure cause",
"exception stack trace",
)
).first_party_analysis,
)?.first_party_analysis,
true,
);
});

View file

@ -233,6 +233,7 @@ export interface EventReport {
* @param startedAt The time this action started executing.
* @param cause Cause of failure (only supply if status is 'failure')
* @param exception Exception (only supply if status is 'failure')
* @returns undefined if an exception was thrown.
*/
export async function createStatusReportBase(
actionName: ActionName,
@ -243,103 +244,112 @@ export async function createStatusReportBase(
logger: Logger,
cause?: string,
exception?: string,
): Promise<StatusReportBase> {
const commitOid = getOptionalInput("sha") || process.env["GITHUB_SHA"] || "";
const ref = await getRef();
const jobRunUUID = process.env[EnvVar.JOB_RUN_UUID] || "";
const workflowRunID = getWorkflowRunID();
const workflowRunAttempt = getWorkflowRunAttempt();
const workflowName = process.env["GITHUB_WORKFLOW"] || "";
const jobName = process.env["GITHUB_JOB"] || "";
const analysis_key = await getAnalysisKey();
let workflowStartedAt = process.env[EnvVar.WORKFLOW_STARTED_AT];
if (workflowStartedAt === undefined) {
workflowStartedAt = actionStartedAt.toISOString();
core.exportVariable(EnvVar.WORKFLOW_STARTED_AT, workflowStartedAt);
}
const runnerOs = getRequiredEnvParam("RUNNER_OS");
const codeQlCliVersion = getCachedCodeQlVersion();
const actionRef = process.env["GITHUB_ACTION_REF"];
const testingEnvironment = process.env[EnvVar.TESTING_ENVIRONMENT] || "";
// re-export the testing environment variable so that it is available to subsequent steps,
// even if it was only set for this step
if (testingEnvironment !== "") {
core.exportVariable(EnvVar.TESTING_ENVIRONMENT, testingEnvironment);
}
const statusReport: StatusReportBase = {
action_name: actionName,
action_oid: "unknown", // TODO decide if it's possible to fill this in
action_ref: actionRef,
action_started_at: actionStartedAt.toISOString(),
action_version: getActionVersion(),
analysis_key,
build_mode: config?.buildMode,
commit_oid: commitOid,
first_party_analysis: isFirstPartyAnalysis(actionName),
job_name: jobName,
job_run_uuid: jobRunUUID,
ref,
runner_os: runnerOs,
started_at: workflowStartedAt,
status,
testing_environment: testingEnvironment,
workflow_name: workflowName,
workflow_run_attempt: workflowRunAttempt,
workflow_run_id: workflowRunID,
};
): Promise<StatusReportBase | undefined> {
try {
statusReport.actions_event_name = getWorkflowEventName();
const commitOid =
getOptionalInput("sha") || process.env["GITHUB_SHA"] || "";
const ref = await getRef();
const jobRunUUID = process.env[EnvVar.JOB_RUN_UUID] || "";
const workflowRunID = getWorkflowRunID();
const workflowRunAttempt = getWorkflowRunAttempt();
const workflowName = process.env["GITHUB_WORKFLOW"] || "";
const jobName = process.env["GITHUB_JOB"] || "";
const analysis_key = await getAnalysisKey();
let workflowStartedAt = process.env[EnvVar.WORKFLOW_STARTED_AT];
if (workflowStartedAt === undefined) {
workflowStartedAt = actionStartedAt.toISOString();
core.exportVariable(EnvVar.WORKFLOW_STARTED_AT, workflowStartedAt);
}
const runnerOs = getRequiredEnvParam("RUNNER_OS");
const codeQlCliVersion = getCachedCodeQlVersion();
const actionRef = process.env["GITHUB_ACTION_REF"] || "";
const testingEnvironment = process.env[EnvVar.TESTING_ENVIRONMENT] || "";
// re-export the testing environment variable so that it is available to subsequent steps,
// even if it was only set for this step
if (testingEnvironment !== "") {
core.exportVariable(EnvVar.TESTING_ENVIRONMENT, testingEnvironment);
}
const statusReport: StatusReportBase = {
action_name: actionName,
action_oid: "unknown", // TODO decide if it's possible to fill this in
action_ref: actionRef,
action_started_at: actionStartedAt.toISOString(),
action_version: getActionVersion(),
analysis_key,
build_mode: config?.buildMode,
commit_oid: commitOid,
first_party_analysis: isFirstPartyAnalysis(actionName),
job_name: jobName,
job_run_uuid: jobRunUUID,
ref,
runner_os: runnerOs,
started_at: workflowStartedAt,
status,
testing_environment: testingEnvironment,
workflow_name: workflowName,
workflow_run_attempt: workflowRunAttempt,
workflow_run_id: workflowRunID,
};
try {
statusReport.actions_event_name = getWorkflowEventName();
} catch (e) {
logger.warning(`Could not determine the workflow event name: ${e}.`);
}
if (config) {
statusReport.languages = config.languages.join(",");
}
if (diskInfo) {
statusReport.runner_available_disk_space_bytes =
diskInfo.numAvailableBytes;
statusReport.runner_total_disk_space_bytes = diskInfo.numTotalBytes;
}
// Add optional parameters
if (cause) {
statusReport.cause = cause;
}
if (exception) {
statusReport.exception = exception;
}
if (
status === "success" ||
status === "failure" ||
status === "aborted" ||
status === "user-error"
) {
statusReport.completed_at = new Date().toISOString();
}
const matrix = getRequiredInput("matrix");
if (matrix) {
statusReport.matrix_vars = matrix;
}
if ("RUNNER_ARCH" in process.env) {
// RUNNER_ARCH is available only in GHES 3.4 and later
// Values other than X86, X64, ARM, or ARM64 are discarded server side
statusReport.runner_arch = process.env["RUNNER_ARCH"];
}
if (runnerOs === "Windows" || runnerOs === "macOS") {
statusReport.runner_os_release = os.release();
}
if (codeQlCliVersion !== undefined) {
statusReport.codeql_version = codeQlCliVersion.version;
}
const imageVersion = process.env["ImageVersion"];
if (imageVersion) {
statusReport.runner_image_version = imageVersion;
}
return statusReport;
} catch (e) {
logger.warning(`Could not determine the workflow event name: ${e}.`);
logger.warning(
`Caught an exception while gathering information for telemetry: ${e}. Will skip sending status report.`,
);
return undefined;
}
if (config) {
statusReport.languages = config.languages.join(",");
}
if (diskInfo) {
statusReport.runner_available_disk_space_bytes = diskInfo.numAvailableBytes;
statusReport.runner_total_disk_space_bytes = diskInfo.numTotalBytes;
}
// Add optional parameters
if (cause) {
statusReport.cause = cause;
}
if (exception) {
statusReport.exception = exception;
}
if (
status === "success" ||
status === "failure" ||
status === "aborted" ||
status === "user-error"
) {
statusReport.completed_at = new Date().toISOString();
}
const matrix = getRequiredInput("matrix");
if (matrix) {
statusReport.matrix_vars = matrix;
}
if ("RUNNER_ARCH" in process.env) {
// RUNNER_ARCH is available only in GHES 3.4 and later
// Values other than X86, X64, ARM, or ARM64 are discarded server side
statusReport.runner_arch = process.env["RUNNER_ARCH"];
}
if (runnerOs === "Windows" || runnerOs === "macOS") {
statusReport.runner_os_release = os.release();
}
if (codeQlCliVersion !== undefined) {
statusReport.codeql_version = codeQlCliVersion.version;
}
const imageVersion = process.env["ImageVersion"];
if (imageVersion) {
statusReport.runner_image_version = imageVersion;
}
return statusReport;
}
const OUT_OF_DATE_MSG =

View file

@ -41,11 +41,13 @@ async function sendSuccessStatusReport(
await checkDiskUsage(),
logger,
);
const statusReport: UploadSarifStatusReport = {
...statusReportBase,
...uploadStats,
};
await sendStatusReport(statusReport);
if (statusReportBase !== undefined) {
const statusReport: UploadSarifStatusReport = {
...statusReportBase,
...uploadStats,
};
await sendStatusReport(statusReport);
}
}
async function run() {
@ -56,16 +58,17 @@ async function run() {
const gitHubVersion = await getGitHubVersion();
checkActionVersion(getActionVersion(), gitHubVersion);
await sendStatusReport(
await createStatusReportBase(
ActionName.UploadSarif,
"starting",
startedAt,
undefined,
await checkDiskUsage(),
logger,
),
const startingStatusReportBase = await createStatusReportBase(
ActionName.UploadSarif,
"starting",
startedAt,
undefined,
await checkDiskUsage(),
logger,
);
if (startingStatusReportBase !== undefined) {
await sendStatusReport(startingStatusReportBase);
}
try {
const uploadResult = await upload_lib.uploadFromActions(
@ -96,18 +99,20 @@ async function run() {
const message = error.message;
core.setFailed(message);
console.log(error);
await sendStatusReport(
await createStatusReportBase(
ActionName.UploadSarif,
getActionsStatus(error),
startedAt,
undefined,
await checkDiskUsage(),
logger,
message,
error.stack,
),
const errorStatusReportBase = await createStatusReportBase(
ActionName.UploadSarif,
getActionsStatus(error),
startedAt,
undefined,
await checkDiskUsage(),
logger,
message,
error.stack,
);
if (errorStatusReportBase !== undefined) {
await sendStatusReport(errorStatusReportBase);
}
return;
}
}