mirror of
https://github.com/github/codeql-action.git
synced 2026-10-21 02:01:27 +00:00
668 lines
22 KiB
TypeScript
668 lines
22 KiB
TypeScript
import * as fs from "fs";
|
|
import path from "path";
|
|
|
|
import * as github from "@actions/github";
|
|
|
|
import * as actionsUtil from "./actions-util";
|
|
import { CodeScanning, RiskAssessment } from "./analyses";
|
|
import { getApiClient, getGitHubVersion } from "./api-client";
|
|
import { CodeQL, getCodeQL } from "./codeql";
|
|
import {
|
|
hasConfigKey,
|
|
type AbortedInitConfig,
|
|
type ActionConfig,
|
|
} from "./config/action-config";
|
|
import {
|
|
Config,
|
|
isCodeScanningEnabled,
|
|
isRiskAssessmentEnabled,
|
|
} from "./config-utils";
|
|
import {
|
|
getArtifactSuffix,
|
|
getArtifactUploaderClient,
|
|
sanitizeArtifactName,
|
|
type UploadDebugArtifactsConfig,
|
|
} from "./debug-artifacts";
|
|
import * as dependencyCaching from "./dependency-caching";
|
|
import { getTempDiagnosticPath } from "./diagnostics";
|
|
import { EnvVar, ReadOnlyEnv } from "./environment";
|
|
import { Feature, FeatureEnablement } from "./feature-flags";
|
|
import { Logger } from "./logging";
|
|
import { OverlayDatabaseMode } from "./overlay/overlay-database-mode";
|
|
import {
|
|
createOverlayStatus,
|
|
OverlayStatus,
|
|
saveOverlayStatus,
|
|
} from "./overlay/status";
|
|
import { RepositoryNwo, getRepositoryNwo } from "./repository";
|
|
import { JobStatus } from "./status-report";
|
|
import * as uploadLib from "./upload-lib";
|
|
import {
|
|
checkDiskUsage,
|
|
delay,
|
|
Failure,
|
|
getErrorMessage,
|
|
getRequiredEnvParam,
|
|
parseMatrixInput,
|
|
Result,
|
|
shouldSkipGenerateFailedSarif,
|
|
shouldSkipSarifUpload,
|
|
Success,
|
|
wrapError,
|
|
} from "./util";
|
|
import {
|
|
getCategoryInputOrThrow,
|
|
getRepositoryRootOrThrow,
|
|
getUploadInputOrThrow,
|
|
getWorkflow,
|
|
} from "./workflow";
|
|
|
|
export interface UploadFailedSarifResult extends uploadLib.UploadStatusReport {
|
|
/** If there was an error while uploading a failed run, this is its message. */
|
|
upload_failed_run_error?: string;
|
|
/** If there was an error while uploading a failed run, this is its stack trace. */
|
|
upload_failed_run_stack_trace?: string;
|
|
/** Reason why we did not upload a SARIF payload with `executionSuccessful: false`. */
|
|
upload_failed_run_skipped_because?: string;
|
|
|
|
/** The internal ID of SARIF analysis. */
|
|
sarifID?: string;
|
|
}
|
|
|
|
export interface JobStatusReport {
|
|
job_status: JobStatus;
|
|
}
|
|
|
|
export interface DependencyCachingUsageReport {
|
|
dependency_caching_usage?: dependencyCaching.DependencyCachingUsageReport;
|
|
}
|
|
|
|
function createFailedUploadFailedSarifResult(
|
|
error: unknown,
|
|
): UploadFailedSarifResult {
|
|
const wrappedError = wrapError(error);
|
|
return {
|
|
upload_failed_run_error: wrappedError.message,
|
|
upload_failed_run_stack_trace: wrappedError.stack,
|
|
};
|
|
}
|
|
|
|
/** Records details about a SARIF file that contains information about a failed analysis. */
|
|
interface FailedSarifInfo {
|
|
sarifFile: string;
|
|
category: string | undefined;
|
|
checkoutPath: string;
|
|
}
|
|
|
|
/** The minimal Action configuration required to prepare and upload a failed SARIF. */
|
|
export type FailedSarifConfig = ActionConfig<
|
|
"codeQLCmd" | "tempDir" | "analysisKinds" | "languages"
|
|
>;
|
|
|
|
/**
|
|
* Tries to prepare a SARIF file that contains information about a failed analysis.
|
|
*
|
|
* @returns Either information about the SARIF file that was produced, or a reason why it couldn't be produced.
|
|
*/
|
|
async function prepareFailedSarif(
|
|
logger: Logger,
|
|
features: FeatureEnablement,
|
|
config: FailedSarifConfig,
|
|
): Promise<Result<FailedSarifInfo, UploadFailedSarifResult>> {
|
|
if (!config.codeQLCmd) {
|
|
return new Failure({
|
|
upload_failed_run_skipped_because: "CodeQL command not found",
|
|
});
|
|
}
|
|
const jobName = getRequiredEnvParam("GITHUB_JOB");
|
|
const matrix = parseMatrixInput(actionsUtil.getRequiredInput("matrix"));
|
|
|
|
if (shouldSkipGenerateFailedSarif()) {
|
|
return new Failure({
|
|
upload_failed_run_skipped_because: "SARIF upload is disabled",
|
|
});
|
|
}
|
|
|
|
if (isRiskAssessmentEnabled(config)) {
|
|
if (config.languages.length !== 1) {
|
|
return new Failure({
|
|
upload_failed_run_skipped_because:
|
|
"Unexpectedly, the configuration is not for a single language.",
|
|
});
|
|
}
|
|
|
|
// We can make these assumptions for risk assessments.
|
|
const language = config.languages[0];
|
|
const category = `/language:${language}`;
|
|
const checkoutPath = ".";
|
|
const result = await generateFailedSarif(
|
|
logger,
|
|
features,
|
|
config,
|
|
category,
|
|
checkoutPath,
|
|
`../codeql-failed-sarif-${language}${RiskAssessment.sarifExtension}`,
|
|
);
|
|
return new Success(result);
|
|
} else {
|
|
const workflow = await getWorkflow(logger);
|
|
const shouldUpload = getUploadInputOrThrow(workflow, jobName, matrix);
|
|
if (
|
|
!["always", "failure-only"].includes(
|
|
actionsUtil.getUploadValue(shouldUpload),
|
|
)
|
|
) {
|
|
return new Failure({
|
|
upload_failed_run_skipped_because: "SARIF upload is disabled",
|
|
});
|
|
}
|
|
const category = getCategoryInputOrThrow(workflow, jobName, matrix);
|
|
|
|
// Try to determine the path at which the repository that we failed to analyse is checked out at.
|
|
// We need this to relativise the paths in the SARIF.
|
|
const checkoutPath = getRepositoryRootOrThrow(
|
|
workflow,
|
|
jobName,
|
|
matrix,
|
|
config,
|
|
);
|
|
|
|
const result = await generateFailedSarif(
|
|
logger,
|
|
features,
|
|
config,
|
|
category,
|
|
checkoutPath,
|
|
);
|
|
return new Success(result);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates a SARIF file for a failed run, i.e. an analysis where no SARIF was produced as a result of the analysis.
|
|
*
|
|
* Not intended to be called directly, other than by `prepareFailedSarif`.
|
|
*
|
|
* @param category The analysis category to include in the SARIF.
|
|
* @param checkoutPath The path where the repository was checked out at.
|
|
* @param sarifFile The SARIF file to produce.
|
|
*/
|
|
export async function generateFailedSarif(
|
|
logger: Logger,
|
|
features: FeatureEnablement,
|
|
config: ActionConfig<"codeQLCmd" | "tempDir">,
|
|
category: string | undefined,
|
|
checkoutPath: string,
|
|
sarifFile?: string,
|
|
) {
|
|
const databasePath = config.dbLocation;
|
|
const codeql = await getCodeQL(logger, config.codeQLCmd);
|
|
|
|
// Set the filename for the SARIF file if not already set.
|
|
if (sarifFile === undefined) {
|
|
sarifFile = "../codeql-failed-run.sarif";
|
|
}
|
|
|
|
// If there is no database or the feature flag is off, we run 'export diagnostics'
|
|
if (
|
|
databasePath === undefined ||
|
|
!(await features.getValue(Feature.ExportDiagnosticsEnabled, codeql))
|
|
) {
|
|
// If we don't have a database path, then we may have diagnostics that were written to a
|
|
// temporary location. Get the path that they would have been written to and check whether
|
|
// it exists. If so, pass it on to the CLI. The CLI will not complain if we give it a
|
|
// path that doesn't exist, so we are just being extra defensive here.
|
|
let diagnosticDir: string | undefined = getTempDiagnosticPath(
|
|
config.tempDir,
|
|
);
|
|
|
|
if (!fs.existsSync(diagnosticDir)) {
|
|
diagnosticDir = undefined;
|
|
}
|
|
|
|
await codeql.diagnosticsExport(sarifFile, category, config, diagnosticDir);
|
|
} else {
|
|
// We call 'database export-diagnostics' to find any per-database diagnostics.
|
|
await codeql.databaseExportDiagnostics(databasePath, sarifFile, category);
|
|
}
|
|
|
|
return { sarifFile, category, checkoutPath };
|
|
}
|
|
|
|
/**
|
|
* Upload a failed SARIF file if we can verify that SARIF upload is enabled and determine the SARIF
|
|
* category for the workflow.
|
|
*/
|
|
async function maybeUploadFailedSarif(
|
|
config: FailedSarifConfig,
|
|
repositoryNwo: RepositoryNwo,
|
|
features: FeatureEnablement,
|
|
logger: Logger,
|
|
): Promise<UploadFailedSarifResult> {
|
|
const failedSarifResult = await prepareFailedSarif(logger, features, config);
|
|
|
|
if (failedSarifResult.isFailure()) {
|
|
return failedSarifResult.value;
|
|
}
|
|
|
|
const failedSarif = failedSarifResult.value;
|
|
|
|
logger.info(`Uploading failed SARIF file ${failedSarif.sarifFile}`);
|
|
const uploadResult = await uploadLib.uploadFiles(
|
|
config,
|
|
failedSarif.sarifFile,
|
|
failedSarif.checkoutPath,
|
|
failedSarif.category,
|
|
features,
|
|
logger,
|
|
CodeScanning,
|
|
);
|
|
await uploadLib.waitForProcessing(
|
|
repositoryNwo,
|
|
uploadResult.sarifID,
|
|
logger,
|
|
{ isUnsuccessfulExecution: true },
|
|
);
|
|
|
|
// If SARIF upload is disabled for testing, `uploadFiles` will not have uploaded the failed
|
|
// SARIF file and instead will have written it to disk. Upload it as a workflow artifact so that
|
|
// we can inspect it in validation jobs.
|
|
if (uploadResult.sarifID && shouldSkipSarifUpload()) {
|
|
await uploadSarifArtifact(logger, "test-mode-sarif", failedSarif);
|
|
}
|
|
|
|
return uploadResult
|
|
? { ...uploadResult.statusReport, sarifID: uploadResult.sarifID }
|
|
: {};
|
|
}
|
|
|
|
/** Uploads a failed SARIF file as workflow artifact, if it can be generated. */
|
|
async function maybeUploadFailedSarifArtifact(
|
|
config: FailedSarifConfig,
|
|
features: FeatureEnablement,
|
|
logger: Logger,
|
|
): Promise<UploadFailedSarifResult> {
|
|
const failedSarifResult = await prepareFailedSarif(logger, features, config);
|
|
|
|
if (failedSarifResult.isFailure()) {
|
|
return failedSarifResult.value;
|
|
}
|
|
|
|
const failedSarif = failedSarifResult.value;
|
|
return uploadSarifArtifact(logger, "sarif-artifact", failedSarif);
|
|
}
|
|
|
|
/** Uploads a failed SARIF file as workflow artifact. */
|
|
async function uploadSarifArtifact(
|
|
logger: Logger,
|
|
prefix: string,
|
|
failedSarif: FailedSarifInfo,
|
|
): Promise<UploadFailedSarifResult> {
|
|
logger.startGroup(`Uploading failed SARIF file as artifact`);
|
|
logger.info(`Uploading '${failedSarif.sarifFile}' as artifact`);
|
|
|
|
try {
|
|
const gitHubVersion = await getGitHubVersion();
|
|
const client = await getArtifactUploaderClient(logger, gitHubVersion.type);
|
|
|
|
const suffix = getArtifactSuffix(actionsUtil.getOptionalInput("matrix"));
|
|
const name = sanitizeArtifactName(`${prefix}-${suffix}`);
|
|
await client.uploadArtifact(
|
|
name,
|
|
[path.normalize(failedSarif.sarifFile)],
|
|
path.normalize(path.dirname(failedSarif.sarifFile)),
|
|
);
|
|
|
|
return { sarifID: name };
|
|
} finally {
|
|
logger.endGroup();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Tries to upload a SARIF file with information about the run, if it failed.
|
|
*
|
|
* @param config The CodeQL Action configuration.
|
|
* @param repositoryNwo The name and owner of the repository.
|
|
* @param features Information about enabled features.
|
|
* @param logger The logger to use.
|
|
* @returns The results of uploading the SARIF file for the failure.
|
|
*/
|
|
export async function tryUploadSarifIfRunFailed(
|
|
config: FailedSarifConfig,
|
|
repositoryNwo: RepositoryNwo,
|
|
features: FeatureEnablement,
|
|
logger: Logger,
|
|
): Promise<UploadFailedSarifResult> {
|
|
// There's nothing to do here if the analysis succeeded.
|
|
if (process.env[EnvVar.ANALYZE_DID_COMPLETE_SUCCESSFULLY] === "true") {
|
|
return {
|
|
upload_failed_run_skipped_because:
|
|
"Analyze Action completed successfully",
|
|
};
|
|
}
|
|
|
|
try {
|
|
// Only upload the failed SARIF to Code scanning if Code scanning is enabled.
|
|
if (isCodeScanningEnabled(config)) {
|
|
return await maybeUploadFailedSarif(
|
|
config,
|
|
repositoryNwo,
|
|
features,
|
|
logger,
|
|
);
|
|
} else if (isRiskAssessmentEnabled(config)) {
|
|
return await maybeUploadFailedSarifArtifact(config, features, logger);
|
|
} else {
|
|
return {
|
|
upload_failed_run_skipped_because:
|
|
"No analysis kind that supports failed SARIF uploads is enabled.",
|
|
};
|
|
}
|
|
} catch (e) {
|
|
logger.debug(
|
|
`Failed to upload a SARIF file for this failed CodeQL code scanning run. ${e}`,
|
|
);
|
|
return createFailedUploadFailedSarifResult(e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handles the majority of the `post-init` step logic which, depending on the configuration,
|
|
* mainly involves uploading a SARIF file with information about the failed run, debug
|
|
* artifacts, and performing clean-up operations.
|
|
*
|
|
* @param uploadAllAvailableDebugArtifacts A function with which to upload debug artifacts.
|
|
* @param printDebugLogs A function with which to print debug logs.
|
|
* @param codeql The CodeQL CLI instance.
|
|
* @param config The CodeQL Action configuration.
|
|
* @param repositoryNwo The name and owner of the repository.
|
|
* @param features Information about enabled features.
|
|
* @param jobStatus The status of the job, as reported by the Actions runtime environment.
|
|
* @param env The environment to read variables from.
|
|
* @param logger The logger to use.
|
|
* @returns The results of uploading the SARIF file for the failure.
|
|
*/
|
|
export async function uploadFailureInfo(
|
|
uploadAllAvailableDebugArtifacts: (
|
|
codeql: CodeQL,
|
|
config: UploadDebugArtifactsConfig,
|
|
logger: Logger,
|
|
) => Promise<void>,
|
|
printDebugLogs: (logger: Logger, config: Partial<Config>) => Promise<void>,
|
|
codeql: CodeQL,
|
|
config: Config | AbortedInitConfig,
|
|
repositoryNwo: RepositoryNwo,
|
|
features: FeatureEnablement,
|
|
jobStatus: string | undefined,
|
|
env: ReadOnlyEnv,
|
|
logger: Logger,
|
|
): Promise<UploadFailedSarifResult> {
|
|
await recordOverlayStatus(codeql, config, features, jobStatus, env, logger);
|
|
|
|
const uploadFailedSarifResult = await tryUploadSarifIfRunFailed(
|
|
config,
|
|
repositoryNwo,
|
|
features,
|
|
logger,
|
|
);
|
|
|
|
if (uploadFailedSarifResult.upload_failed_run_skipped_because) {
|
|
logger.debug(
|
|
"Won't upload a failed SARIF file for this CodeQL analysis because: " +
|
|
`${uploadFailedSarifResult.upload_failed_run_skipped_because}.`,
|
|
);
|
|
}
|
|
// Throw an error if in integration tests, we expected to upload a SARIF file for a failed run
|
|
// but we didn't upload anything.
|
|
if (
|
|
process.env["CODEQL_ACTION_EXPECT_UPLOAD_FAILED_SARIF"] === "true" &&
|
|
!uploadFailedSarifResult.raw_upload_size_bytes
|
|
) {
|
|
const error = JSON.stringify(uploadFailedSarifResult);
|
|
throw new Error(
|
|
"Expected to upload a failed SARIF file for this CodeQL code scanning run, " +
|
|
`but the result was instead ${error}.`,
|
|
);
|
|
}
|
|
|
|
if (process.env["CODEQL_ACTION_EXPECT_UPLOAD_FAILED_SARIF"] === "true") {
|
|
if (!github.context.payload.pull_request?.head.repo.fork) {
|
|
await removeUploadedSarif(uploadFailedSarifResult, logger);
|
|
} else {
|
|
logger.info(
|
|
"Skipping deletion of failed SARIF because the workflow was triggered from a fork of " +
|
|
"codeql-action and doesn't have the appropriate permissions for deletion.",
|
|
);
|
|
}
|
|
}
|
|
|
|
// Upload appropriate Actions artifacts for debugging
|
|
if (config.debugMode && hasConfigKey(config, "dbLocation")) {
|
|
logger.info(
|
|
"Debug mode is on. Uploading available database bundles and logs as Actions debugging artifacts...",
|
|
);
|
|
await uploadAllAvailableDebugArtifacts(codeql, config, logger);
|
|
await printDebugLogs(logger, config);
|
|
}
|
|
|
|
if (actionsUtil.isSelfHostedRunner() && config.dbLocation) {
|
|
try {
|
|
fs.rmSync(config.dbLocation, {
|
|
recursive: true,
|
|
force: true,
|
|
maxRetries: 3,
|
|
});
|
|
logger.info(
|
|
`Cleaned up database cluster directory ${config.dbLocation}.`,
|
|
);
|
|
} catch (e) {
|
|
logger.warning(
|
|
`Failed to clean up database cluster directory ${config.dbLocation}. Details: ${e}`,
|
|
);
|
|
}
|
|
} else if (config.dbLocation === undefined) {
|
|
logger.debug(
|
|
"Skipping cleanup of database cluster directory since the configuration does not include a database path.",
|
|
);
|
|
} else {
|
|
logger.debug(
|
|
"Skipping cleanup of database cluster directory since we are running on a GitHub-hosted " +
|
|
"runner which will be automatically cleaned up.",
|
|
);
|
|
}
|
|
|
|
return uploadFailedSarifResult;
|
|
}
|
|
|
|
/**
|
|
* Whether one of the CodeQL Actions reported an error for this job, which means the analysis
|
|
* genuinely failed.
|
|
*
|
|
* Note that the converse does not hold: an Action that is terminated abruptly, or that fails before
|
|
* it can gather telemetry, does not get to report anything.
|
|
*/
|
|
function didCodeQlReportError(env: ReadOnlyEnv): boolean {
|
|
const jobStatus = env.getOptional(EnvVar.JOB_STATUS);
|
|
return (
|
|
jobStatus === JobStatus.FailureStatus ||
|
|
jobStatus === JobStatus.ConfigErrorStatus
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Whether the job status tells us anything about whether the analysis itself would have succeeded.
|
|
*
|
|
* We check for the statuses we know to be meaningful rather than excluding the ones that are not,
|
|
* so that a status we do not recognise is treated as inconclusive.
|
|
*/
|
|
function isConclusiveJobStatus(jobStatus: string | undefined): boolean {
|
|
switch (jobStatus?.trim().toLowerCase()) {
|
|
case "failure":
|
|
case "success":
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* If overlay base database creation was attempted but the analysis did not complete
|
|
* successfully, save the failure status to the Actions cache so that subsequent runs
|
|
* can skip overlay analysis until something changes (e.g. a new CodeQL version).
|
|
*/
|
|
async function recordOverlayStatus(
|
|
codeql: CodeQL,
|
|
config: Partial<Config>,
|
|
features: FeatureEnablement,
|
|
jobStatus: string | undefined,
|
|
env: ReadOnlyEnv,
|
|
logger: Logger,
|
|
) {
|
|
if (
|
|
config.overlayDatabaseMode !== OverlayDatabaseMode.OverlayBase ||
|
|
env.getOptional(EnvVar.ANALYZE_DID_COMPLETE_SUCCESSFULLY) === "true" ||
|
|
!(await features.getValue(Feature.OverlayAnalysisStatusSave))
|
|
) {
|
|
return;
|
|
} else if (config.languages === undefined) {
|
|
logger.debug(
|
|
"Not recording overlay status, because the configuration contains no 'languages'.",
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Only record a failure when the job outcome tells us something about the analysis. A cancelled
|
|
// job, or a status we do not recognise, says nothing about whether the analysis would have
|
|
// succeeded, so recording a failure would disable overlay analysis needlessly. We still record
|
|
// one if a CodeQL Action reported an error before the job ended.
|
|
if (!isConclusiveJobStatus(jobStatus) && !didCodeQlReportError(env)) {
|
|
logger.info(
|
|
"Not recording an improved incremental analysis failure for this job because the job " +
|
|
`status (${jobStatus ?? "unset"}) does not tell us whether the analysis itself failed.`,
|
|
);
|
|
return;
|
|
}
|
|
|
|
const checkRunIdInput = actionsUtil.getOptionalInput("check-run-id");
|
|
const checkRunId =
|
|
checkRunIdInput !== undefined ? parseInt(checkRunIdInput, 10) : undefined;
|
|
|
|
const overlayStatus: OverlayStatus = createOverlayStatus(
|
|
{
|
|
attemptedToBuildOverlayBaseDatabase: true,
|
|
builtOverlayBaseDatabase: false,
|
|
},
|
|
checkRunId !== undefined && checkRunId >= 0 ? checkRunId : undefined,
|
|
);
|
|
|
|
const diskUsage = await checkDiskUsage(logger);
|
|
if (diskUsage === undefined) {
|
|
logger.warning(
|
|
"Unable to save overlay status to the Actions cache because the available disk space could not be determined.",
|
|
);
|
|
return;
|
|
}
|
|
|
|
const saved = await saveOverlayStatus(
|
|
codeql,
|
|
config.languages,
|
|
diskUsage,
|
|
overlayStatus,
|
|
logger,
|
|
);
|
|
|
|
const blurb =
|
|
"This job attempted to run with improved incremental analysis but it did not complete successfully. " +
|
|
"One possible reason for this is disk space constraints, since improved incremental analysis can " +
|
|
"require a significant amount of disk space for some repositories.";
|
|
|
|
if (saved) {
|
|
logger.error(
|
|
`${blurb} ` +
|
|
"This failure has been recorded in the Actions cache, so the next CodeQL analysis will run " +
|
|
"without improved incremental analysis. If you want to enable improved incremental analysis, " +
|
|
"try increasing the disk space available to the runner. " +
|
|
"If that doesn't help, contact GitHub Support for further assistance.",
|
|
);
|
|
} else {
|
|
logger.error(
|
|
`${blurb} ` +
|
|
"The attempt to save this failure status to the Actions cache failed. The Action will attempt to " +
|
|
"run with improved incremental analysis again.",
|
|
);
|
|
}
|
|
}
|
|
|
|
async function removeUploadedSarif(
|
|
uploadFailedSarifResult: UploadFailedSarifResult,
|
|
logger: Logger,
|
|
) {
|
|
const sarifID = uploadFailedSarifResult.sarifID;
|
|
if (sarifID) {
|
|
logger.startGroup("Deleting failed SARIF upload");
|
|
logger.info(
|
|
`In test mode, therefore deleting the failed analysis to avoid impacting tool status for the Action repository. SARIF ID to delete: ${sarifID}.`,
|
|
);
|
|
const client = getApiClient();
|
|
|
|
try {
|
|
const repositoryNwo = getRepositoryNwo();
|
|
|
|
// Wait to make sure the analysis is ready for download before requesting it.
|
|
await delay(5000);
|
|
|
|
// Get the analysis associated with the uploaded sarif
|
|
const analysisInfo = await client.request(
|
|
"GET /repos/:owner/:repo/code-scanning/analyses?sarif_id=:sarif_id",
|
|
{
|
|
owner: repositoryNwo.owner,
|
|
repo: repositoryNwo.repo,
|
|
sarif_id: sarifID,
|
|
},
|
|
);
|
|
|
|
// Delete the analysis.
|
|
if (analysisInfo.data.length === 1) {
|
|
const analysis = analysisInfo.data[0];
|
|
logger.info(`Analysis ID to delete: ${analysis.id}.`);
|
|
try {
|
|
await client.request(
|
|
"DELETE /repos/:owner/:repo/code-scanning/analyses/:analysis_id?confirm_delete",
|
|
{
|
|
owner: repositoryNwo.owner,
|
|
repo: repositoryNwo.repo,
|
|
analysis_id: analysis.id,
|
|
},
|
|
);
|
|
logger.info(`Analysis deleted.`);
|
|
} catch (e) {
|
|
const origMessage = getErrorMessage(e);
|
|
const newMessage = origMessage.includes(
|
|
"No analysis found for analysis ID",
|
|
)
|
|
? `Analysis ${analysis.id} does not exist. It was likely already deleted.`
|
|
: origMessage;
|
|
throw new Error(newMessage);
|
|
}
|
|
} else {
|
|
throw new Error(
|
|
`Expected to find exactly one analysis with sarif_id ${sarifID}. Found ${analysisInfo.data.length}.`,
|
|
);
|
|
}
|
|
} catch (e) {
|
|
throw new Error(
|
|
`Failed to delete uploaded SARIF analysis. Reason: ${getErrorMessage(
|
|
e,
|
|
)}`,
|
|
);
|
|
} finally {
|
|
logger.endGroup();
|
|
}
|
|
} else {
|
|
logger.warning(
|
|
"Could not delete the uploaded SARIF analysis because a SARIF ID wasn't provided by the API when uploading the SARIF file.",
|
|
);
|
|
}
|
|
}
|