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
No known key found for this signature in database
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] ## [UNRELEASED]
- Update default CodeQL bundle version to 2.17.2. [#2270](https://github.com/github/codeql-action/pull/2270) - 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 ## 3.25.3 - 25 Apr 2024
@ -30,7 +31,7 @@ No user facing changes.
- The `setup-python-dependencies` input to the `init` Action - The `setup-python-dependencies` input to the `init` Action
- The `CODEQL_ACTION_DISABLE_PYTHON_DEPENDENCY_INSTALLATION` environment variable - The `CODEQL_ACTION_DISABLE_PYTHON_DEPENDENCY_INSTALLATION` environment variable
We recommend removing any references to these from your workflows. For more information, see the release notes for CodeQL Action v3.23.0 and v2.23.0. We recommend removing any references to these from your workflows. For more information, see the release notes for CodeQL Action v3.23.0 and v2.23.0.
- Automatically overwrite an existing database if found on the filesystem. [#2229](https://github.com/github/codeql-action/pull/2229) - Automatically overwrite an existing database if found on the filesystem. [#2229](https://github.com/github/codeql-action/pull/2229)
- Bump the minimum CodeQL bundle version to 2.12.6. [#2232](https://github.com/github/codeql-action/pull/2232) - Bump the minimum CodeQL bundle version to 2.12.6. [#2232](https://github.com/github/codeql-action/pull/2232)

47
lib/workflow.js generated
View file

@ -35,9 +35,6 @@ const yaml = __importStar(require("js-yaml"));
const api = __importStar(require("./api-client")); const api = __importStar(require("./api-client"));
const environment_1 = require("./environment"); const environment_1 = require("./environment");
const util_1 = require("./util"); const util_1 = require("./util");
function isObject(o) {
return o !== null && typeof o === "object";
}
const GLOB_PATTERN = new RegExp("(\\*\\*?)"); const GLOB_PATTERN = new RegExp("(\\*\\*?)");
function escapeRegExp(string) { function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
@ -144,35 +141,31 @@ async function getWorkflowErrors(doc, codeql) {
} }
} }
} }
let missingPush = false; // If there is no push trigger, we will not be able to analyze the default branch.
if (doc.on === undefined) { // So add a warning to the user to add a push trigger.
// this is not a valid config // 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.
else if (typeof doc.on === "string") { const hasPushTrigger = hasWorkflowTrigger("push", doc);
if (doc.on === "pull_request") { const hasPullRequestTrigger = hasWorkflowTrigger("pull_request", doc);
missingPush = true; const hasWorkflowCallTrigger = hasWorkflowTrigger("workflow_call", doc);
} if (hasPullRequestTrigger && !hasPushTrigger && !hasWorkflowCallTrigger) {
}
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) {
errors.push(exports.WorkflowErrors.MissingPushHook); errors.push(exports.WorkflowErrors.MissingPushHook);
} }
return errors; return errors;
} }
exports.getWorkflowErrors = getWorkflowErrors; 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) { async function validateWorkflow(codeql, logger) {
let workflow; let workflow;
try { 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"] on: ["push"]
`), await (0, codeql_1.getCodeQLForTesting)()), [])); `), 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) => { (0, ava_1.default)("getCategoryInputOrThrow returns category for simple workflow with category", (t) => {
process.env["GITHUB_REPOSITORY"] = "github/codeql-action-fake-repository"; process.env["GITHUB_REPOSITORY"] = "github/codeql-action-fake-repository";
t.is((0, workflow_1.getCategoryInputOrThrow)(yaml.load(` 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) => { test("getCategoryInputOrThrow returns category for simple workflow with category", (t) => {
process.env["GITHUB_REPOSITORY"] = "github/codeql-action-fake-repository"; process.env["GITHUB_REPOSITORY"] = "github/codeql-action-fake-repository";
t.is( t.is(

View file

@ -47,10 +47,6 @@ export interface Workflow {
on?: string | string[] | WorkflowTriggers; on?: string | string[] | WorkflowTriggers;
} }
function isObject(o: unknown): o is object {
return o !== null && typeof o === "object";
}
const GLOB_PATTERN = new RegExp("(\\*\\*?)"); const GLOB_PATTERN = new RegExp("(\\*\\*?)");
function escapeRegExp(string) { 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) { if (hasPullRequestTrigger && !hasPushTrigger && !hasWorkflowCallTrigger) {
// 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) {
errors.push(WorkflowErrors.MissingPushHook); errors.push(WorkflowErrors.MissingPushHook);
} }
return errors; 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( export async function validateWorkflow(
codeql: CodeQL, codeql: CodeQL,
logger: Logger, logger: Logger,