Skip to content

Commit cc6b983

Browse files
authored
Merge pull request #4122 from github/henrymercer/friendly-potato
Don't record an overlay status when the job was cancelled
2 parents 9fddc16 + 38dd4a0 commit cc6b983

5 files changed

Lines changed: 157 additions & 4 deletions

File tree

init/action.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,13 @@ inputs:
164164
[Internal] The ID of the check run, as provided by the Actions runtime environment. Do not set this value manually.
165165
default: ${{ job.check_run_id }}
166166
required: false
167+
job-status:
168+
description: >-
169+
[Internal] The status of the job, as provided by the Actions runtime environment. This is how the
170+
post step learns whether the job as a whole succeeded, failed, or was cancelled. Do not set this
171+
value manually.
172+
default: ${{ job.status }}
173+
required: false
167174
outputs:
168175
codeql-path:
169176
description: The path of the CodeQL binary used for analysis

lib/entry-points.js

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

src/init-action-post-helper.test.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { getRunnerLogger } from "./logging";
1515
import { OverlayDatabaseMode } from "./overlay/overlay-database-mode";
1616
import * as overlayStatus from "./overlay/status";
1717
import { parseRepositoryNwo } from "./repository";
18+
import { JobStatus } from "./status-report";
1819
import {
1920
createFeatures,
2021
createTestConfig,
@@ -58,6 +59,7 @@ test.serial("init-post action with debug mode off", async (t) => {
5859
createTestConfig({ debugMode: false }),
5960
parseRepositoryNwo("github/codeql-action"),
6061
createFeatures([]),
62+
"success",
6163
getRunnerLogger(true),
6264
);
6365

@@ -80,6 +82,7 @@ test.serial("init-post action with debug mode on", async (t) => {
8082
createTestConfig({ debugMode: true }),
8183
parseRepositoryNwo("github/codeql-action"),
8284
createFeatures([]),
85+
"success",
8386
getRunnerLogger(true),
8487
);
8588

@@ -375,6 +378,7 @@ test.serial(
375378
}),
376379
parseRepositoryNwo("github/codeql-action"),
377380
createFeatures([Feature.OverlayAnalysisStatusSave]),
381+
"success",
378382
getRunnerLogger(true),
379383
);
380384

@@ -443,6 +447,7 @@ test.serial(
443447
}),
444448
parseRepositoryNwo("github/codeql-action"),
445449
createFeatures([]),
450+
"success",
446451
getRunnerLogger(true),
447452
);
448453

@@ -480,6 +485,7 @@ test.serial("does not save overlay status when build successful", async (t) => {
480485
}),
481486
parseRepositoryNwo("github/codeql-action"),
482487
createFeatures([Feature.OverlayAnalysisStatusSave]),
488+
"success",
483489
getRunnerLogger(true),
484490
);
485491

@@ -517,6 +523,7 @@ test.serial(
517523
}),
518524
parseRepositoryNwo("github/codeql-action"),
519525
createFeatures([]),
526+
"success",
520527
getRunnerLogger(true),
521528
);
522529

@@ -528,6 +535,94 @@ test.serial(
528535
},
529536
);
530537

538+
/**
539+
* Runs `uploadFailureInfo` for an overlay-base job that did not complete successfully, for a job
540+
* that the Actions runtime environment reports as cancelled.
541+
*/
542+
async function testCancelledOverlayJob({
543+
jobStatus = "cancelled",
544+
codeQlReportedError = false,
545+
}: {
546+
jobStatus?: string;
547+
codeQlReportedError?: boolean;
548+
} = {}) {
549+
return await util.withTmpDir(async (tmpDir) => {
550+
setupActionsVars(tmpDir, tmpDir);
551+
delete process.env[EnvVar.ANALYZE_DID_COMPLETE_SUCCESSFULLY];
552+
if (codeQlReportedError) {
553+
process.env[EnvVar.JOB_STATUS] = JobStatus.FailureStatus;
554+
} else {
555+
delete process.env[EnvVar.JOB_STATUS];
556+
}
557+
558+
sinon.stub(util, "checkDiskUsage").resolves({
559+
numAvailableBytes: 100 * NUM_BYTES_PER_GIB,
560+
numTotalBytes: 200 * NUM_BYTES_PER_GIB,
561+
});
562+
563+
const saveOverlayStatusStub = sinon
564+
.stub(overlayStatus, "saveOverlayStatus")
565+
.resolves(true);
566+
567+
await initActionPostHelper.uploadFailureInfo(
568+
sinon.spy(),
569+
sinon.spy(),
570+
codeql.createStubCodeQL({}),
571+
createTestConfig({
572+
debugMode: false,
573+
languages: ["javascript"],
574+
overlayDatabaseMode: OverlayDatabaseMode.OverlayBase,
575+
}),
576+
parseRepositoryNwo("github/codeql-action"),
577+
createFeatures([Feature.OverlayAnalysisStatusSave]),
578+
jobStatus,
579+
getRunnerLogger(true),
580+
);
581+
582+
return { saveOverlayStatusStub };
583+
});
584+
}
585+
586+
test.serial(
587+
"does not save overlay status when the job was cancelled",
588+
async (t) => {
589+
const { saveOverlayStatusStub } = await testCancelledOverlayJob();
590+
591+
t.true(
592+
saveOverlayStatusStub.notCalled,
593+
"a cancellation tells us nothing about whether the analysis would have succeeded",
594+
);
595+
},
596+
);
597+
598+
test.serial(
599+
"saves overlay status when the job failed rather than being cancelled",
600+
async (t) => {
601+
const { saveOverlayStatusStub } = await testCancelledOverlayJob({
602+
jobStatus: "failure",
603+
});
604+
605+
t.true(
606+
saveOverlayStatusStub.calledOnce,
607+
"only cancellations are treated as unrelated to the analysis",
608+
);
609+
},
610+
);
611+
612+
test.serial(
613+
"saves overlay status when a CodeQL Action reported an error before the run was cancelled",
614+
async (t) => {
615+
const { saveOverlayStatusStub } = await testCancelledOverlayJob({
616+
codeQlReportedError: true,
617+
});
618+
619+
t.true(
620+
saveOverlayStatusStub.calledOnce,
621+
"the analysis genuinely failed, even though the run was later cancelled",
622+
);
623+
},
624+
);
625+
531626
function createTestWorkflow(
532627
steps: workflow.WorkflowJobStep[],
533628
): workflow.Workflow {

src/init-action-post-helper.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,7 @@ export async function tryUploadSarifIfRunFailed(
316316
* @param config The CodeQL Action configuration.
317317
* @param repositoryNwo The name and owner of the repository.
318318
* @param features Information about enabled features.
319+
* @param jobStatus The status of the job, as reported by the Actions runtime environment.
319320
* @param logger The logger to use.
320321
* @returns The results of uploading the SARIF file for the failure.
321322
*/
@@ -331,9 +332,10 @@ export async function uploadFailureInfo(
331332
config: Config,
332333
repositoryNwo: RepositoryNwo,
333334
features: FeatureEnablement,
335+
jobStatus: string | undefined,
334336
logger: Logger,
335337
): Promise<UploadFailedSarifResult> {
336-
await recordOverlayStatus(codeql, config, features, logger);
338+
await recordOverlayStatus(codeql, config, features, jobStatus, logger);
337339

338340
const uploadFailedSarifResult = await tryUploadSarifIfRunFailed(
339341
config,
@@ -412,6 +414,21 @@ export async function uploadFailureInfo(
412414
return uploadFailedSarifResult;
413415
}
414416

417+
/**
418+
* Whether one of the CodeQL Actions reported an error for this job, which means the analysis
419+
* genuinely failed.
420+
*
421+
* Note that the converse does not hold: an Action that is terminated abruptly, or that fails before
422+
* it can gather telemetry, does not get to report anything.
423+
*/
424+
function didCodeQlReportError(): boolean {
425+
const jobStatus = process.env[EnvVar.JOB_STATUS];
426+
return (
427+
jobStatus === JobStatus.FailureStatus ||
428+
jobStatus === JobStatus.ConfigErrorStatus
429+
);
430+
}
431+
415432
/**
416433
* If overlay base database creation was attempted but the analysis did not complete
417434
* successfully, save the failure status to the Actions cache so that subsequent runs
@@ -421,6 +438,7 @@ async function recordOverlayStatus(
421438
codeql: CodeQL,
422439
config: Config,
423440
features: FeatureEnablement,
441+
jobStatus: string | undefined,
424442
logger: Logger,
425443
) {
426444
if (
@@ -431,6 +449,20 @@ async function recordOverlayStatus(
431449
return;
432450
}
433451

452+
// A cancelled run tells us nothing about whether the analysis would have succeeded, so recording
453+
// a failure would disable overlay analysis needlessly. Note that we still record a failure if one
454+
// of our own Actions reported an error before the run was cancelled.
455+
if (
456+
jobStatus?.trim().toLowerCase() === "cancelled" &&
457+
!didCodeQlReportError()
458+
) {
459+
logger.info(
460+
"Not recording an improved incremental analysis failure for this job because the workflow " +
461+
"run was cancelled.",
462+
);
463+
return;
464+
}
465+
434466
const checkRunIdInput = actionsUtil.getOptionalInput("check-run-id");
435467
const checkRunId =
436468
checkRunIdInput !== undefined ? parseInt(checkRunIdInput, 10) : undefined;

src/init-action-post.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import * as core from "@actions/core";
88

99
import {
1010
restoreInputs,
11+
getOptionalInput,
1112
getTemporaryDirectory,
1213
printDebugLogs,
1314
} from "./actions-util";
@@ -55,6 +56,11 @@ async function run(startedAt: Date) {
5556
| undefined;
5657
let dependencyCachingUsage: DependencyCachingUsageReport | undefined;
5758
try {
59+
// Read the job status before restoring inputs, since it is provided by the Actions runtime
60+
// environment for this step and would otherwise be overwritten by the value that the `init`
61+
// Action saw, which is always a success.
62+
const jobStatus = getOptionalInput("job-status");
63+
5864
// Restore inputs from `init` Action.
5965
restoreInputs();
6066

@@ -84,6 +90,7 @@ async function run(startedAt: Date) {
8490
config,
8591
repositoryNwo,
8692
features,
93+
jobStatus,
8794
logger,
8895
);
8996

0 commit comments

Comments
 (0)