Avoid warning on workflow_call triggers

Typically, we warn when there is no `push` trigger in the
workflow file that triggered this run. However, when this
action is triggered by a `workflow_call` event, we assume
there is a custom process for triggering the action and we
don't want to warn in this case.
This commit is contained in:
Andrew Eisenberg
2024-05-06 15:26:55 -07:00
parent 4b812a5dff
commit ca7f194e36
7 changed files with 109 additions and 60 deletions

View File

@@ -7,6 +7,7 @@ Note that the only difference between `v2` and `v3` of the CodeQL Action is the
## [UNRELEASED]
- Update default CodeQL bundle version to 2.17.2. [#2270](https://github.com/github/codeql-action/pull/2270)
- Avoid printing out a warning for a missing `on.push` trigger when the CodeQL Action is triggered via a `workflow_call` event. [#2274](https://github.com/github/codeql-action/pull/2274)
## 3.25.3 - 25 Apr 2024

47
lib/workflow.js generated
View File

@@ -35,9 +35,6 @@ const yaml = __importStar(require("js-yaml"));
const api = __importStar(require("./api-client"));
const environment_1 = require("./environment");
const util_1 = require("./util");
function isObject(o) {
return o !== null && typeof o === "object";
}
const GLOB_PATTERN = new RegExp("(\\*\\*?)");
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
@@ -144,35 +141,31 @@ async function getWorkflowErrors(doc, codeql) {
}
}
}
let missingPush = false;
if (doc.on === undefined) {
// this is not a valid config
}
else if (typeof doc.on === "string") {
if (doc.on === "pull_request") {
missingPush = true;
}
}
else if (Array.isArray(doc.on)) {
const hasPush = doc.on.includes("push");
const hasPullRequest = doc.on.includes("pull_request");
if (hasPullRequest && !hasPush) {
missingPush = true;
}
}
else if (isObject(doc.on)) {
const hasPush = Object.prototype.hasOwnProperty.call(doc.on, "push");
const hasPullRequest = Object.prototype.hasOwnProperty.call(doc.on, "pull_request");
if (!hasPush && hasPullRequest) {
missingPush = true;
}
}
if (missingPush) {
// If there is no push trigger, we will not be able to analyze the default branch.
// So add a warning to the user to add a push trigger.
// If there is a workflow_call trigger, we don't need a push trigger since we assume
// that the workflow_call trigger is called from a workflow that has a push trigger.
const hasPushTrigger = hasWorkflowTrigger("push", doc);
const hasPullRequestTrigger = hasWorkflowTrigger("pull_request", doc);
const hasWorkflowCallTrigger = hasWorkflowTrigger("workflow_call", doc);
if (hasPullRequestTrigger && !hasPushTrigger && !hasWorkflowCallTrigger) {
errors.push(exports.WorkflowErrors.MissingPushHook);
}
return errors;
}
exports.getWorkflowErrors = getWorkflowErrors;
function hasWorkflowTrigger(triggerName, doc) {
if (!doc.on) {
return false;
}
if (typeof doc.on === "string") {
return doc.on === triggerName;
}
if (Array.isArray(doc.on)) {
return doc.on.includes(triggerName);
}
return Object.prototype.hasOwnProperty.call(doc.on, triggerName);
}
async function validateWorkflow(codeql, logger) {
let workflow;
try {

File diff suppressed because one or more lines are too long

23
lib/workflow.test.js generated
View File

@@ -373,6 +373,29 @@ async function testLanguageAliases(t, matrixLanguages, aliases, expectedErrorMes
on: ["push"]
`), await (0, codeql_1.getCodeQLForTesting)()), []));
});
(0, ava_1.default)("getWorkflowErrors() should not report a warning if there is a workflow_call trigger", async (t) => {
const errors = await (0, workflow_1.getWorkflowErrors)(yaml.load(`
name: "CodeQL"
on:
workflow_call:
`), await (0, codeql_1.getCodeQLForTesting)());
t.deepEqual(...errorCodes(errors, []));
});
(0, ava_1.default)("getWorkflowErrors() should not report a warning if there is a workflow_call trigger as a string", async (t) => {
const errors = await (0, workflow_1.getWorkflowErrors)(yaml.load(`
name: "CodeQL"
on: workflow_call
`), await (0, codeql_1.getCodeQLForTesting)());
t.deepEqual(...errorCodes(errors, []));
});
(0, ava_1.default)("getWorkflowErrors() should not report a warning if there is a workflow_call trigger as an array", async (t) => {
const errors = await (0, workflow_1.getWorkflowErrors)(yaml.load(`
name: "CodeQL"
on:
- workflow_call
`), await (0, codeql_1.getCodeQLForTesting)());
t.deepEqual(...errorCodes(errors, []));
});
(0, ava_1.default)("getCategoryInputOrThrow returns category for simple workflow with category", (t) => {
process.env["GITHUB_REPOSITORY"] = "github/codeql-action-fake-repository";
t.is((0, workflow_1.getCategoryInputOrThrow)(yaml.load(`

File diff suppressed because one or more lines are too long

View File

@@ -643,6 +643,44 @@ test("getWorkflowErrors() should not report an error if PRs are totally unconfig
);
});
test("getWorkflowErrors() should not report a warning if there is a workflow_call trigger", async (t) => {
const errors = await getWorkflowErrors(
yaml.load(`
name: "CodeQL"
on:
workflow_call:
`) as Workflow,
await getCodeQLForTesting(),
);
t.deepEqual(...errorCodes(errors, []));
});
test("getWorkflowErrors() should not report a warning if there is a workflow_call trigger as a string", async (t) => {
const errors = await getWorkflowErrors(
yaml.load(`
name: "CodeQL"
on: workflow_call
`) as Workflow,
await getCodeQLForTesting(),
);
t.deepEqual(...errorCodes(errors, []));
});
test("getWorkflowErrors() should not report a warning if there is a workflow_call trigger as an array", async (t) => {
const errors = await getWorkflowErrors(
yaml.load(`
name: "CodeQL"
on:
- workflow_call
`) as Workflow,
await getCodeQLForTesting(),
);
t.deepEqual(...errorCodes(errors, []));
});
test("getCategoryInputOrThrow returns category for simple workflow with category", (t) => {
process.env["GITHUB_REPOSITORY"] = "github/codeql-action-fake-repository";
t.is(

View File

@@ -47,10 +47,6 @@ export interface Workflow {
on?: string | string[] | WorkflowTriggers;
}
function isObject(o: unknown): o is object {
return o !== null && typeof o === "object";
}
const GLOB_PATTERN = new RegExp("(\\*\\*?)");
function escapeRegExp(string) {
@@ -193,39 +189,37 @@ export async function getWorkflowErrors(
}
}
let missingPush = false;
// If there is no push trigger, we will not be able to analyze the default branch.
// So add a warning to the user to add a push trigger.
// If there is a workflow_call trigger, we don't need a push trigger since we assume
// that the workflow_call trigger is called from a workflow that has a push trigger.
const hasPushTrigger = hasWorkflowTrigger("push", doc);
const hasPullRequestTrigger = hasWorkflowTrigger("pull_request", doc);
const hasWorkflowCallTrigger = hasWorkflowTrigger("workflow_call", doc);
if (doc.on === undefined) {
// this is not a valid config
} else if (typeof doc.on === "string") {
if (doc.on === "pull_request") {
missingPush = true;
}
} else if (Array.isArray(doc.on)) {
const hasPush = doc.on.includes("push");
const hasPullRequest = doc.on.includes("pull_request");
if (hasPullRequest && !hasPush) {
missingPush = true;
}
} else if (isObject(doc.on)) {
const hasPush = Object.prototype.hasOwnProperty.call(doc.on, "push");
const hasPullRequest = Object.prototype.hasOwnProperty.call(
doc.on,
"pull_request",
);
if (!hasPush && hasPullRequest) {
missingPush = true;
}
}
if (missingPush) {
if (hasPullRequestTrigger && !hasPushTrigger && !hasWorkflowCallTrigger) {
errors.push(WorkflowErrors.MissingPushHook);
}
return errors;
}
function hasWorkflowTrigger(triggerName: string, doc: Workflow): boolean {
if (!doc.on) {
return false;
}
if (typeof doc.on === "string") {
return doc.on === triggerName;
}
if (Array.isArray(doc.on)) {
return doc.on.includes(triggerName);
}
return Object.prototype.hasOwnProperty.call(doc.on, triggerName);
}
export async function validateWorkflow(
codeql: CodeQL,
logger: Logger,