Make getInputOrThrow throw when it can't find any calls to the Action

This created unexpected behavior with a workflow calling
`codeql-action/analyze` locally.
Therefore, be more conservative with parsing inputs from workflows and
refuse to parse jobs that don't call the specified Action exactly once.
This commit is contained in:
Henry Mercer
2022-12-06 18:13:47 +00:00
parent 9085295c40
commit 4623c8edb6
6 changed files with 39 additions and 45 deletions

26
lib/workflow.js generated
View File

@@ -264,25 +264,23 @@ function getStepsCallingAction(job, actionName) {
* determine that no such input is passed to the Action. * determine that no such input is passed to the Action.
*/ */
function getInputOrThrow(workflow, jobName, actionName, inputName, matrixVars) { function getInputOrThrow(workflow, jobName, actionName, inputName, matrixVars) {
var _a;
const preamble = `Could not get ${inputName} input to ${actionName} since`;
if (!workflow.jobs) { if (!workflow.jobs) {
throw new Error(`Could not get ${inputName} input to ${actionName} since the workflow has no jobs.`); throw new Error(`${preamble} the workflow has no jobs.`);
} }
if (!workflow.jobs[jobName]) { if (!workflow.jobs[jobName]) {
throw new Error(`Could not get ${inputName} input to ${actionName} since the workflow has no job named ${jobName}.`); throw new Error(`${preamble} the workflow has no job named ${jobName}.`);
} }
const inputs = getStepsCallingAction(workflow.jobs[jobName], actionName) const stepsCallingAction = getStepsCallingAction(workflow.jobs[jobName], actionName);
.map((step) => { var _a; return (_a = step.with) === null || _a === void 0 ? void 0 : _a[inputName]; }) if (stepsCallingAction.length === 0) {
.filter((input) => input !== undefined) throw new Error(`${preamble} the ${jobName} job does not call ${actionName}.`);
.map((input) => input);
if (inputs.length === 0) {
return undefined;
} }
if (!inputs.every((input) => input === inputs[0])) { else if (stepsCallingAction.length > 1) {
throw new Error(`Could not get ${inputName} input to ${actionName} since there were multiple steps calling ` + throw new Error(`${preamble} the ${jobName} job calls ${actionName} multiple times.`);
`${actionName} with different values for ${inputName}.`);
} }
let input = inputs[0]; let input = (_a = stepsCallingAction[0].with) === null || _a === void 0 ? void 0 : _a[inputName];
if (matrixVars !== undefined) { if (input !== undefined && matrixVars !== undefined) {
// Make a basic attempt to substitute matrix variables // Make a basic attempt to substitute matrix variables
// First normalize by removing whitespace // First normalize by removing whitespace
input = input.replace(/\${{\s+/, "${{").replace(/\s+}}/, "}}"); input = input.replace(/\${{\s+/, "${{").replace(/\s+}}/, "}}");
@@ -290,7 +288,7 @@ function getInputOrThrow(workflow, jobName, actionName, inputName, matrixVars) {
input = input.replace(`\${{matrix.${key}}}`, value); input = input.replace(`\${{matrix.${key}}}`, value);
} }
} }
if (input.includes("${{")) { if (input !== undefined && input.includes("${{")) {
throw new Error(`Could not get ${inputName} input to ${actionName} since it contained an unrecognized dynamic value.`); throw new Error(`Could not get ${inputName} input to ${actionName} since it contained an unrecognized dynamic value.`);
} }
return input; return input;

File diff suppressed because one or more lines are too long

6
lib/workflow.test.js generated
View File

@@ -435,7 +435,7 @@ function errorCodes(actual, expected) {
"an unrecognized dynamic value.", "an unrecognized dynamic value.",
}); });
}); });
(0, ava_1.default)("getCategoryInputOrThrow throws error for workflow with multiple categories", (t) => { (0, ava_1.default)("getCategoryInputOrThrow throws error for workflow with multiple calls to analyze", (t) => {
t.throws(() => (0, workflow_1.getCategoryInputOrThrow)(yaml.load(` t.throws(() => (0, workflow_1.getCategoryInputOrThrow)(yaml.load(`
jobs: jobs:
analysis: analysis:
@@ -450,8 +450,8 @@ function errorCodes(actual, expected) {
with: with:
category: another-category category: another-category
`), "analysis", {}), { `), "analysis", {}), {
message: "Could not get category input to github/codeql-action/analyze since there were multiple steps " + message: "Could not get category input to github/codeql-action/analyze since the analysis job " +
"calling github/codeql-action/analyze with different values for category.", "calls github/codeql-action/analyze multiple times.",
}); });
}); });
//# sourceMappingURL=workflow.test.js.map //# sourceMappingURL=workflow.test.js.map

File diff suppressed because one or more lines are too long

View File

@@ -646,7 +646,7 @@ test("getCategoryInputOrThrow throws error for workflow with dynamic category",
); );
}); });
test("getCategoryInputOrThrow throws error for workflow with multiple categories", (t) => { test("getCategoryInputOrThrow throws error for workflow with multiple calls to analyze", (t) => {
t.throws( t.throws(
() => () =>
getCategoryInputOrThrow( getCategoryInputOrThrow(
@@ -669,8 +669,8 @@ test("getCategoryInputOrThrow throws error for workflow with multiple categories
), ),
{ {
message: message:
"Could not get category input to github/codeql-action/analyze since there were multiple steps " + "Could not get category input to github/codeql-action/analyze since the analysis job " +
"calling github/codeql-action/analyze with different values for category.", "calls github/codeql-action/analyze multiple times.",
} }
); );
}); });

View File

@@ -327,35 +327,32 @@ function getInputOrThrow(
inputName: string, inputName: string,
matrixVars: { [key: string]: string } | undefined matrixVars: { [key: string]: string } | undefined
) { ) {
const preamble = `Could not get ${inputName} input to ${actionName} since`;
if (!workflow.jobs) { if (!workflow.jobs) {
throw new Error( throw new Error(`${preamble} the workflow has no jobs.`);
`Could not get ${inputName} input to ${actionName} since the workflow has no jobs.`
);
} }
if (!workflow.jobs[jobName]) { if (!workflow.jobs[jobName]) {
throw new Error(`${preamble} the workflow has no job named ${jobName}.`);
}
const stepsCallingAction = getStepsCallingAction(
workflow.jobs[jobName],
actionName
);
if (stepsCallingAction.length === 0) {
throw new Error( throw new Error(
`Could not get ${inputName} input to ${actionName} since the workflow has no job named ${jobName}.` `${preamble} the ${jobName} job does not call ${actionName}.`
);
} else if (stepsCallingAction.length > 1) {
throw new Error(
`${preamble} the ${jobName} job calls ${actionName} multiple times.`
); );
} }
const inputs = getStepsCallingAction(workflow.jobs[jobName], actionName) let input = stepsCallingAction[0].with?.[inputName];
.map((step) => step.with?.[inputName])
.filter((input) => input !== undefined)
.map((input) => input!);
if (inputs.length === 0) { if (input !== undefined && matrixVars !== undefined) {
return undefined;
}
if (!inputs.every((input) => input === inputs[0])) {
throw new Error(
`Could not get ${inputName} input to ${actionName} since there were multiple steps calling ` +
`${actionName} with different values for ${inputName}.`
);
}
let input = inputs[0];
if (matrixVars !== undefined) {
// Make a basic attempt to substitute matrix variables // Make a basic attempt to substitute matrix variables
// First normalize by removing whitespace // First normalize by removing whitespace
input = input.replace(/\${{\s+/, "${{").replace(/\s+}}/, "}}"); input = input.replace(/\${{\s+/, "${{").replace(/\s+}}/, "}}");
@@ -363,8 +360,7 @@ function getInputOrThrow(
input = input.replace(`\${{matrix.${key}}}`, value); input = input.replace(`\${{matrix.${key}}}`, value);
} }
} }
if (input !== undefined && input.includes("${{")) {
if (input.includes("${{")) {
throw new Error( throw new Error(
`Could not get ${inputName} input to ${actionName} since it contained an unrecognized dynamic value.` `Could not get ${inputName} input to ${actionName} since it contained an unrecognized dynamic value.`
); );