Send overall job status in init-post status report (#2097)

Co-authored-by: Henry Mercer <henry@henrymercer.name>
This commit is contained in:
Angela P Wen
2024-01-26 05:11:46 -08:00
committed by GitHub
parent 16150320c5
commit 61bf02577c
12 changed files with 139 additions and 8 deletions

View File

@@ -1,3 +1,9 @@
/**
* Environment variables used by the CodeQL Action.
*
* We recommend prefixing environment variables with `CODEQL_ACTION_`
* to reduce the risk that they are overwritten by other steps.
*/
export enum EnvVar {
/** Whether the `analyze` Action completes successfully. */
ANALYZE_DID_COMPLETE_SUCCESSFULLY = "CODEQL_ACTION_ANALYZE_DID_COMPLETE_SUCCESSFULLY",
@@ -35,6 +41,9 @@ export enum EnvVar {
/** UUID representing the current job run. */
JOB_RUN_UUID = "JOB_RUN_UUID",
/** Status for the entire job, submitted to the status report in `init-post` */
JOB_STATUS = "CODEQL_ACTION_JOB_STATUS",
ODASA_TRACER_CONFIGURATION = "ODASA_TRACER_CONFIGURATION",
/**

View File

@@ -1,3 +1,4 @@
import * as core from "@actions/core";
import * as github from "@actions/github";
import * as actionsUtil from "./actions-util";
@@ -8,6 +9,7 @@ import { EnvVar } from "./environment";
import { Feature, FeatureEnablement } from "./feature-flags";
import { Logger } from "./logging";
import { RepositoryNwo, parseRepositoryNwo } from "./repository";
import { JobStatus } from "./status-report";
import * as uploadLib from "./upload-lib";
import {
delay,
@@ -36,6 +38,10 @@ export interface UploadFailedSarifResult extends uploadLib.UploadStatusReport {
sarifID?: string;
}
export interface JobStatusReport {
job_status: JobStatus;
}
function createFailedUploadFailedSarifResult(
error: unknown,
): UploadFailedSarifResult {
@@ -121,6 +127,15 @@ export async function tryUploadSarifIfRunFailed(
logger: Logger,
): Promise<UploadFailedSarifResult> {
if (process.env[EnvVar.ANALYZE_DID_COMPLETE_SUCCESSFULLY] !== "true") {
// If analyze didn't complete successfully and the job status hasn't
// already been set to Failure/ConfigurationError previously, this
// means that something along the way failed in a step that is not
// owned by the Action, for example a manual build step. We
// consider this a configuration error.
core.exportVariable(
EnvVar.JOB_STATUS,
process.env[EnvVar.JOB_STATUS] ?? JobStatus.ConfigurationError,
);
try {
return await maybeUploadFailedSarif(
config,
@@ -135,6 +150,10 @@ export async function tryUploadSarifIfRunFailed(
return createFailedUploadFailedSarifResult(e);
}
} else {
core.exportVariable(
EnvVar.JOB_STATUS,
process.env[EnvVar.JOB_STATUS] ?? JobStatus.Success,
);
return {
upload_failed_run_skipped_because:
"Analyze Action completed successfully",
@@ -282,3 +301,20 @@ async function removeUploadedSarif(
);
}
}
/**
* Returns the final job status sent in the `init-post` Action, based on the
* current value of the JOB_STATUS environment variable. If the variable is
* unset, or if its value is not one of the JobStatus enum values, returns
* Unknown. Otherwise it returns the status set in the environment variable.
*/
export function getFinalJobStatus(): JobStatus {
const jobStatusFromEnvironment = process.env[EnvVar.JOB_STATUS];
if (
!jobStatusFromEnvironment ||
!Object.values(JobStatus).includes(jobStatusFromEnvironment as JobStatus)
) {
return JobStatus.Unknown;
}
return jobStatusFromEnvironment as JobStatus;
}

View File

@@ -28,7 +28,8 @@ import {
interface InitPostStatusReport
extends StatusReportBase,
initActionPostHelper.UploadFailedSarifResult {}
initActionPostHelper.UploadFailedSarifResult,
initActionPostHelper.JobStatusReport {}
async function runWrapper() {
const startedAt = new Date();
@@ -83,6 +84,7 @@ async function runWrapper() {
const statusReport: InitPostStatusReport = {
...statusReportBase,
...uploadFailedSarifResult,
job_status: initActionPostHelper.getFinalJobStatus(),
};
await sendStatusReport(statusReport);
}

View File

@@ -32,12 +32,20 @@ export type ActionName =
| "upload-sarif";
export type ActionStatus =
| "aborted"
| "aborted" // Only used in the init Action, if init failed before initializing the tracer due to something other than a configuration error.
| "failure"
| "starting"
| "success"
| "user-error";
/** Overall status of the entire job. String values match the Hydro schema. */
export enum JobStatus {
Unknown = "JOB_STATUS_UNKNOWN",
Success = "JOB_STATUS_SUCCESS",
Failure = "JOB_STATUS_FAILURE",
ConfigurationError = "JOB_STATUS_CONFIGURATION_ERROR",
}
export interface StatusReportBase {
/** Name of the action being executed. */
action_name: ActionName;
@@ -133,6 +141,25 @@ export function getActionsStatus(
}
}
/**
* Sets the overall job status environment variable to configuration error
* or failure, unless it's already been set to one of these values in a
* previous step.
*/
function setJobStatusIfUnsuccessful(actionStatus: ActionStatus) {
if (actionStatus === "user-error") {
core.exportVariable(
EnvVar.JOB_STATUS,
process.env[EnvVar.JOB_STATUS] ?? JobStatus.ConfigurationError,
);
} else if (actionStatus === "failure" || actionStatus === "aborted") {
core.exportVariable(
EnvVar.JOB_STATUS,
process.env[EnvVar.JOB_STATUS] ?? JobStatus.Failure,
);
}
}
// Any status report may include an array of EventReports associated with it.
export interface EventReport {
/** Time this event ended. */
@@ -273,6 +300,8 @@ const INCOMPATIBLE_MSG =
export async function sendStatusReport<S extends StatusReportBase>(
statusReport: S,
): Promise<boolean> {
setJobStatusIfUnsuccessful(statusReport.status);
const statusReportJSON = JSON.stringify(statusReport);
core.debug(`Sending status report: ${statusReportJSON}`);
// If in test mode we don't want to upload the results