Compare commits

...

5 Commits

Author SHA1 Message Date
Henry Mercer 0f3834178f Wait 15 seconds before checking API 2026-01-23 15:59:55 +00:00
Henry Mercer 625ae85c0a Pass check run ID via input 2026-01-23 15:55:38 +00:00
Henry Mercer a753f07e38 Add delay to init for testing 2026-01-23 14:30:11 +00:00
Henry Mercer 6ea1dea708 Add testing workflow 2026-01-23 14:26:57 +00:00
Henry Mercer 994478a5be Experiment with checking job cancellation 2026-01-23 14:25:33 +00:00
6 changed files with 153 additions and 4 deletions
+2 -3
View File
@@ -9,9 +9,6 @@ env:
GO111MODULE: auto
on:
push:
branches:
- main
- releases/v*
pull_request:
types:
- opened
@@ -59,6 +56,8 @@ jobs:
setup-kotlin: 'true'
- uses: ./../action/init
id: init
# force cancellation for testing
timeout-minutes: 1
with:
languages: javascript
tools: ${{ steps.prepare-test.outputs.tools-url }}
+6
View File
@@ -159,6 +159,12 @@ inputs:
description: >-
Explicitly enable or disable caching of project build dependencies.
required: false
check-run-id:
description: >-
TEMPORARY: The check run ID of the current job (from job.check_run_id context).
Used for testing cancellation detection via the API.
required: false
default: ${{ job.check_run_id }}
outputs:
codeql-path:
description: The path of the CodeQL binary used for analysis
+60
View File
@@ -132907,6 +132907,65 @@ function getFinalJobStatus() {
}
// src/init-action-post.ts
async function testCancellationDetection() {
const logger = getActionsLogger();
try {
const apiClient = getApiClient();
const runId = getWorkflowRunID();
const attemptNumber = getWorkflowRunAttempt();
const checkRunIdInput = process.env["INPUT_CHECK-RUN-ID"] || "";
const repositoryNwo = getRepositoryNwo();
if (!checkRunIdInput) {
logger.warning(
"[Cancellation Test] check-run-id input not provided, skipping"
);
return;
}
await new Promise((resolve8) => setTimeout(resolve8, 15e3));
const checkRunId = parseInt(checkRunIdInput, 10);
logger.info(
`[Cancellation Test] Querying jobs API for run ${runId}, attempt ${attemptNumber}, check_run_id ${checkRunId}`
);
const response = await apiClient.rest.actions.listJobsForWorkflowRunAttempt(
{
owner: repositoryNwo.owner,
repo: repositoryNwo.repo,
run_id: runId,
attempt_number: attemptNumber
}
);
const jobs = response.data.jobs;
const currentJob = jobs.find((j) => j.id === checkRunId);
if (currentJob) {
logger.info(
`[Cancellation Test] Current job status: ${currentJob.status}, conclusion: ${currentJob.conclusion}`
);
const steps = currentJob.steps || [];
for (const step of steps) {
logger.info(
`[Cancellation Test] Step "${step.name}": status=${step.status}, conclusion=${step.conclusion}`
);
}
const hasCancelledStep = steps.some(
(step) => step.conclusion === "cancelled"
);
logger.info(
`[Cancellation Test] Has cancelled step: ${hasCancelledStep}`
);
} else {
logger.warning(
`[Cancellation Test] Could not find job with check_run_id ${checkRunId} in API response`
);
logger.info(
`[Cancellation Test] Available jobs: ${jobs.map((j) => `${j.id} (${j.name})`).join(", ")}`
);
}
} catch (error3) {
logger.warning(
`[Cancellation Test] Failed to query API: ${error3 instanceof Error ? error3.message : String(error3)}`
);
}
}
async function run2(startedAt) {
const logger = getActionsLogger();
let config;
@@ -132914,6 +132973,7 @@ async function run2(startedAt) {
let dependencyCachingUsage;
try {
restoreInputs();
await testCancellationDetection();
const gitHubVersion = await getGitHubVersion();
checkGitHubVersionInRange(gitHubVersion, logger);
const repositoryNwo = getRepositoryNwo();
+1
View File
@@ -92569,6 +92569,7 @@ async function run(startedAt) {
logger.info(`Job run UUID is ${jobRunUuid}.`);
core13.exportVariable("JOB_RUN_UUID" /* JOB_RUN_UUID */, jobRunUuid);
core13.exportVariable("CODEQL_ACTION_INIT_HAS_RUN" /* INIT_ACTION_HAS_RUN */, "true");
await new Promise((resolve9) => setTimeout(resolve9, 5 * 60 * 1e3));
configFile = getOptionalInput("config-file");
sourceRoot = path15.resolve(
getRequiredEnvParam("GITHUB_WORKSPACE"),
+81 -1
View File
@@ -10,8 +10,10 @@ import {
restoreInputs,
getTemporaryDirectory,
printDebugLogs,
getWorkflowRunID,
getWorkflowRunAttempt,
} from "./actions-util";
import { getGitHubVersion } from "./api-client";
import { getApiClient, getGitHubVersion } from "./api-client";
import { CachingKind } from "./caching-utils";
import { getCodeQL } from "./codeql";
import { Config, getConfig } from "./config-utils";
@@ -42,6 +44,81 @@ interface InitPostStatusReport
initActionPostHelper.JobStatusReport,
initActionPostHelper.DependencyCachingUsageReport {}
/**
* TEMPORARY: Test function to check if the GitHub API can detect workflow cancellation.
* This queries the Jobs API to see the current job's status and step conclusions.
*/
async function testCancellationDetection(): Promise<void> {
const logger = getActionsLogger();
try {
const apiClient = getApiClient();
const runId = getWorkflowRunID();
const attemptNumber = getWorkflowRunAttempt();
const checkRunIdInput = process.env["INPUT_CHECK-RUN-ID"] || "";
const repositoryNwo = getRepositoryNwo();
if (!checkRunIdInput) {
logger.warning(
"[Cancellation Test] check-run-id input not provided, skipping",
);
return;
}
// Wait 15 seconds to ensure that the API has caught up with the job status
await new Promise((resolve) => setTimeout(resolve, 15000));
const checkRunId = parseInt(checkRunIdInput, 10);
logger.info(
`[Cancellation Test] Querying jobs API for run ${runId}, attempt ${attemptNumber}, check_run_id ${checkRunId}`,
);
const response = await apiClient.rest.actions.listJobsForWorkflowRunAttempt(
{
owner: repositoryNwo.owner,
repo: repositoryNwo.repo,
run_id: runId,
attempt_number: attemptNumber,
},
);
const jobs = response.data.jobs;
const currentJob = jobs.find((j) => j.id === checkRunId);
if (currentJob) {
logger.info(
`[Cancellation Test] Current job status: ${currentJob.status}, conclusion: ${currentJob.conclusion}`,
);
// Log each step's status
const steps = currentJob.steps || [];
for (const step of steps) {
logger.info(
`[Cancellation Test] Step "${step.name}": status=${step.status}, conclusion=${step.conclusion}`,
);
}
// Check if any step shows cancelled
const hasCancelledStep = steps.some(
(step) => step.conclusion === "cancelled",
);
logger.info(
`[Cancellation Test] Has cancelled step: ${hasCancelledStep}`,
);
} else {
logger.warning(
`[Cancellation Test] Could not find job with check_run_id ${checkRunId} in API response`,
);
logger.info(
`[Cancellation Test] Available jobs: ${jobs.map((j) => `${j.id} (${j.name})`).join(", ")}`,
);
}
} catch (error) {
logger.warning(
`[Cancellation Test] Failed to query API: ${error instanceof Error ? error.message : String(error)}`,
);
}
}
async function run(startedAt: Date) {
// To capture errors appropriately, keep as much code within the try-catch as
// possible, and only use safe functions outside.
@@ -56,6 +133,9 @@ async function run(startedAt: Date) {
// Restore inputs from `init` Action.
restoreInputs();
// TEMPORARY: Test cancellation detection via API
await testCancellationDetection();
const gitHubVersion = await getGitHubVersion();
checkGitHubVersionInRange(gitHubVersion, logger);
+3
View File
@@ -251,6 +251,9 @@ async function run(startedAt: Date) {
core.exportVariable(EnvVar.INIT_ACTION_HAS_RUN, "true");
// wait 5 minutes for testing
await new Promise((resolve) => setTimeout(resolve, 5 * 60 * 1000));
configFile = getOptionalInput("config-file");
// path.resolve() respects the intended semantics of source-root. If