Move checkWorkflow to workflow.ts

This commit is contained in:
Michael B. Gale
2025-10-27 17:01:23 +00:00
parent 50601762ea
commit 55c083790a
11 changed files with 1789 additions and 1860 deletions

View File

@@ -40,7 +40,6 @@ import { loadPropertiesFromApi } from "./feature-flags/properties";
import {
checkInstallPython311,
checkPacksForOverlayCompatibility,
checkWorkflow,
cleanupDatabaseClusterDirectory,
initCodeQL,
initConfig,
@@ -87,6 +86,7 @@ import {
getErrorMessage,
BuildMode,
} from "./util";
import { checkWorkflow } from "./workflow";
/**
* Sends a status report indicating that the `init` Action is starting.

View File

@@ -2,104 +2,23 @@ import * as fs from "fs";
import path from "path";
import test, { ExecutionContext } from "ava";
import * as sinon from "sinon";
import * as actionsUtil from "./actions-util";
import { createStubCodeQL } from "./codeql";
import { EnvVar } from "./environment";
import {
checkPacksForOverlayCompatibility,
checkWorkflow,
cleanupDatabaseClusterDirectory,
} from "./init";
import { KnownLanguage } from "./languages";
import {
LoggedMessage,
checkExpectedLogMessages,
createTestConfig,
getRecordingLogger,
setupTests,
} from "./testing-utils";
import { ConfigurationError, withTmpDir } from "./util";
import * as workflow from "./workflow";
setupTests(test);
test("checkWorkflow - validates workflow if `SKIP_WORKFLOW_VALIDATION` is not set", async (t) => {
const messages: LoggedMessage[] = [];
const codeql = createStubCodeQL({});
sinon.stub(actionsUtil, "isDynamicWorkflow").returns(false);
const validateWorkflow = sinon.stub(workflow, "validateWorkflow");
validateWorkflow.resolves(undefined);
await checkWorkflow(getRecordingLogger(messages), codeql);
t.assert(
validateWorkflow.calledOnce,
"`checkWorkflow` unexpectedly did not call `validateWorkflow`",
);
checkExpectedLogMessages(t, messages, [
"Detected no issues with the code scanning workflow.",
]);
});
test("checkWorkflow - logs problems with workflow validation", async (t) => {
const messages: LoggedMessage[] = [];
const codeql = createStubCodeQL({});
sinon.stub(actionsUtil, "isDynamicWorkflow").returns(false);
const validateWorkflow = sinon.stub(workflow, "validateWorkflow");
validateWorkflow.resolves("problem");
await checkWorkflow(getRecordingLogger(messages), codeql);
t.assert(
validateWorkflow.calledOnce,
"`checkWorkflow` unexpectedly did not call `validateWorkflow`",
);
checkExpectedLogMessages(t, messages, [
"Unable to validate code scanning workflow: problem",
]);
});
test("checkWorkflow - skips validation if `SKIP_WORKFLOW_VALIDATION` is `true`", async (t) => {
process.env[EnvVar.SKIP_WORKFLOW_VALIDATION] = "true";
const messages: LoggedMessage[] = [];
const codeql = createStubCodeQL({});
sinon.stub(actionsUtil, "isDynamicWorkflow").returns(false);
const validateWorkflow = sinon.stub(workflow, "validateWorkflow");
await checkWorkflow(getRecordingLogger(messages), codeql);
t.assert(
validateWorkflow.notCalled,
"`checkWorkflow` called `validateWorkflow` unexpectedly",
);
t.is(messages.length, 0);
});
test("checkWorkflow - skips validation for `dynamic` workflows", async (t) => {
const messages: LoggedMessage[] = [];
const codeql = createStubCodeQL({});
const isDynamicWorkflow = sinon
.stub(actionsUtil, "isDynamicWorkflow")
.returns(true);
const validateWorkflow = sinon.stub(workflow, "validateWorkflow");
await checkWorkflow(getRecordingLogger(messages), codeql);
t.assert(isDynamicWorkflow.calledOnce);
t.assert(
validateWorkflow.notCalled,
"`checkWorkflow` called `validateWorkflow` unexpectedly",
);
t.is(messages.length, 0);
});
test("cleanupDatabaseClusterDirectory cleans up where possible", async (t) => {
await withTmpDir(async (tmpDir: string) => {
const dbLocation = path.resolve(tmpDir, "dbs");

View File

@@ -1,20 +1,14 @@
import * as fs from "fs";
import * as path from "path";
import * as core from "@actions/core";
import * as toolrunner from "@actions/exec/lib/toolrunner";
import * as io from "@actions/io";
import * as yaml from "js-yaml";
import {
getOptionalInput,
isDynamicWorkflow,
isSelfHostedRunner,
} from "./actions-util";
import { getOptionalInput, isSelfHostedRunner } from "./actions-util";
import { GitHubApiDetails } from "./api-client";
import { CodeQL, setupCodeQL } from "./codeql";
import * as configUtils from "./config-utils";
import { EnvVar } from "./environment";
import { CodeQLDefaultVersionInfo, FeatureEnablement } from "./feature-flags";
import { KnownLanguage, Language } from "./languages";
import { Logger, withGroupAsync } from "./logging";
@@ -22,33 +16,6 @@ import { ToolsSource } from "./setup-codeql";
import { ZstdAvailability } from "./tar";
import { ToolsDownloadStatusReport } from "./tools-download";
import * as util from "./util";
import { validateWorkflow } from "./workflow";
/**
* A wrapper around `validateWorkflow` which reports the outcome.
*
* @param logger The logger to use.
* @param codeql The CodeQL instance.
*/
export async function checkWorkflow(logger: Logger, codeql: CodeQL) {
// Check the workflow for problems, unless `SKIP_WORKFLOW_VALIDATION` is `true`
// or the workflow trigger is `dynamic`.
if (
!isDynamicWorkflow() &&
process.env[EnvVar.SKIP_WORKFLOW_VALIDATION] !== "true"
) {
core.startGroup("Validating workflow");
const validateWorkflowResult = await validateWorkflow(codeql, logger);
if (validateWorkflowResult === undefined) {
logger.info("Detected no issues with the code scanning workflow.");
} else {
logger.warning(
`Unable to validate code scanning workflow: ${validateWorkflowResult}`,
);
}
core.endGroup();
}
}
export async function initCodeQL(
toolsInput: string | undefined,

View File

@@ -2,9 +2,17 @@ import test, { ExecutionContext } from "ava";
import * as yaml from "js-yaml";
import * as sinon from "sinon";
import { getCodeQLForTesting } from "./codeql";
import { setupTests } from "./testing-utils";
import * as actionsUtil from "./actions-util";
import { createStubCodeQL, getCodeQLForTesting } from "./codeql";
import { EnvVar } from "./environment";
import {
checkExpectedLogMessages,
getRecordingLogger,
LoggedMessage,
setupTests,
} from "./testing-utils";
import {
checkWorkflow,
CodedError,
formatWorkflowCause,
formatWorkflowErrors,
@@ -13,6 +21,7 @@ import {
Workflow,
WorkflowErrors,
} from "./workflow";
import * as workflow from "./workflow";
function errorCodes(
actual: CodedError[],
@@ -870,3 +879,78 @@ test("getCategoryInputOrThrow throws error for workflow with multiple calls to a
},
);
});
test("checkWorkflow - validates workflow if `SKIP_WORKFLOW_VALIDATION` is not set", async (t) => {
const messages: LoggedMessage[] = [];
const codeql = createStubCodeQL({});
sinon.stub(actionsUtil, "isDynamicWorkflow").returns(false);
const validateWorkflow = sinon.stub(workflow.internal, "validateWorkflow");
validateWorkflow.resolves(undefined);
await checkWorkflow(getRecordingLogger(messages), codeql);
t.assert(
validateWorkflow.calledOnce,
"`checkWorkflow` unexpectedly did not call `validateWorkflow`",
);
checkExpectedLogMessages(t, messages, [
"Detected no issues with the code scanning workflow.",
]);
});
test("checkWorkflow - logs problems with workflow validation", async (t) => {
const messages: LoggedMessage[] = [];
const codeql = createStubCodeQL({});
sinon.stub(actionsUtil, "isDynamicWorkflow").returns(false);
const validateWorkflow = sinon.stub(workflow.internal, "validateWorkflow");
validateWorkflow.resolves("problem");
await checkWorkflow(getRecordingLogger(messages), codeql);
t.assert(
validateWorkflow.calledOnce,
"`checkWorkflow` unexpectedly did not call `validateWorkflow`",
);
checkExpectedLogMessages(t, messages, [
"Unable to validate code scanning workflow: problem",
]);
});
test("checkWorkflow - skips validation if `SKIP_WORKFLOW_VALIDATION` is `true`", async (t) => {
process.env[EnvVar.SKIP_WORKFLOW_VALIDATION] = "true";
const messages: LoggedMessage[] = [];
const codeql = createStubCodeQL({});
sinon.stub(actionsUtil, "isDynamicWorkflow").returns(false);
const validateWorkflow = sinon.stub(workflow.internal, "validateWorkflow");
await checkWorkflow(getRecordingLogger(messages), codeql);
t.assert(
validateWorkflow.notCalled,
"`checkWorkflow` called `validateWorkflow` unexpectedly",
);
t.is(messages.length, 0);
});
test("checkWorkflow - skips validation for `dynamic` workflows", async (t) => {
const messages: LoggedMessage[] = [];
const codeql = createStubCodeQL({});
const isDynamicWorkflow = sinon
.stub(actionsUtil, "isDynamicWorkflow")
.returns(true);
const validateWorkflow = sinon.stub(workflow.internal, "validateWorkflow");
await checkWorkflow(getRecordingLogger(messages), codeql);
t.assert(isDynamicWorkflow.calledOnce);
t.assert(
validateWorkflow.notCalled,
"`checkWorkflow` called `validateWorkflow` unexpectedly",
);
t.is(messages.length, 0);
});

View File

@@ -5,8 +5,10 @@ import zlib from "zlib";
import * as core from "@actions/core";
import * as yaml from "js-yaml";
import { isDynamicWorkflow } from "./actions-util";
import * as api from "./api-client";
import { CodeQL } from "./codeql";
import { EnvVar } from "./environment";
import { Logger } from "./logging";
import {
getRequiredEnvParam,
@@ -216,7 +218,7 @@ function hasWorkflowTrigger(triggerName: string, doc: Workflow): boolean {
return Object.prototype.hasOwnProperty.call(doc.on, triggerName);
}
export async function validateWorkflow(
async function validateWorkflow(
codeql: CodeQL,
logger: Logger,
): Promise<undefined | string> {
@@ -462,3 +464,36 @@ export function getCheckoutPathInputOrThrow(
) || getRequiredEnvParam("GITHUB_WORKSPACE") // if unspecified, checkout_path defaults to ${{ github.workspace }}
);
}
/**
* A wrapper around `validateWorkflow` which reports the outcome.
*
* @param logger The logger to use.
* @param codeql The CodeQL instance.
*/
export async function checkWorkflow(logger: Logger, codeql: CodeQL) {
// Check the workflow for problems, unless `SKIP_WORKFLOW_VALIDATION` is `true`
// or the workflow trigger is `dynamic`.
if (
!isDynamicWorkflow() &&
process.env[EnvVar.SKIP_WORKFLOW_VALIDATION] !== "true"
) {
core.startGroup("Validating workflow");
const validateWorkflowResult = await internal.validateWorkflow(
codeql,
logger,
);
if (validateWorkflowResult === undefined) {
logger.info("Detected no issues with the code scanning workflow.");
} else {
logger.warning(
`Unable to validate code scanning workflow: ${validateWorkflowResult}`,
);
}
core.endGroup();
}
}
export const internal = {
validateWorkflow,
};