mirror of
https://github.com/github/codeql-action.git
synced 2026-01-05 06:00:32 +08:00
Generalize getCategoryInputOrThrow to arbitrary inputs
This commit is contained in:
@@ -6,7 +6,7 @@ import {
|
||||
CodedError,
|
||||
formatWorkflowCause,
|
||||
formatWorkflowErrors,
|
||||
tryGetCategoryInput,
|
||||
getCategoryInputOrThrow,
|
||||
getWorkflowErrors,
|
||||
patternIsSuperset,
|
||||
Workflow,
|
||||
@@ -524,9 +524,9 @@ test("getWorkflowErrors() should not report an error if PRs are totally unconfig
|
||||
);
|
||||
});
|
||||
|
||||
test("tryGetCategoryInput returns category for simple workflow with category", (t) => {
|
||||
test("getCategoryInputOrThrow returns category for simple workflow with category", (t) => {
|
||||
t.is(
|
||||
tryGetCategoryInput(
|
||||
getCategoryInputOrThrow(
|
||||
yaml.load(`
|
||||
jobs:
|
||||
analysis:
|
||||
@@ -544,9 +544,9 @@ test("tryGetCategoryInput returns category for simple workflow with category", (
|
||||
);
|
||||
});
|
||||
|
||||
test("tryGetCategoryInput returns undefined for simple workflow without category", (t) => {
|
||||
test("getCategoryInputOrThrow returns undefined for simple workflow without category", (t) => {
|
||||
t.is(
|
||||
tryGetCategoryInput(
|
||||
getCategoryInputOrThrow(
|
||||
yaml.load(`
|
||||
jobs:
|
||||
analysis:
|
||||
@@ -562,9 +562,9 @@ test("tryGetCategoryInput returns undefined for simple workflow without category
|
||||
);
|
||||
});
|
||||
|
||||
test("tryGetCategoryInput finds category for workflow with language matrix", (t) => {
|
||||
test("getCategoryInputOrThrow finds category for workflow with language matrix", (t) => {
|
||||
t.is(
|
||||
tryGetCategoryInput(
|
||||
getCategoryInputOrThrow(
|
||||
yaml.load(`
|
||||
jobs:
|
||||
analysis:
|
||||
@@ -587,10 +587,10 @@ test("tryGetCategoryInput finds category for workflow with language matrix", (t)
|
||||
);
|
||||
});
|
||||
|
||||
test("tryGetCategoryInput throws error for workflow with dynamic category", (t) => {
|
||||
test("getCategoryInputOrThrow throws error for workflow with dynamic category", (t) => {
|
||||
t.throws(
|
||||
() =>
|
||||
tryGetCategoryInput(
|
||||
getCategoryInputOrThrow(
|
||||
yaml.load(`
|
||||
jobs:
|
||||
analysis:
|
||||
@@ -605,15 +605,16 @@ test("tryGetCategoryInput throws error for workflow with dynamic category", (t)
|
||||
),
|
||||
{
|
||||
message:
|
||||
"Could not get category input since it contained an unrecognized dynamic value.",
|
||||
"Could not get category input to github/codeql-action/analyze since it contained " +
|
||||
"an unrecognized dynamic value.",
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test("tryGetCategoryInput throws error for workflow with multiple categories", (t) => {
|
||||
test("getCategoryInputOrThrow throws error for workflow with multiple categories", (t) => {
|
||||
t.throws(
|
||||
() =>
|
||||
tryGetCategoryInput(
|
||||
getCategoryInputOrThrow(
|
||||
yaml.load(`
|
||||
jobs:
|
||||
analysis:
|
||||
@@ -632,7 +633,8 @@ test("tryGetCategoryInput throws error for workflow with multiple categories", (
|
||||
),
|
||||
{
|
||||
message:
|
||||
"Could not get category input since multiple categories were specified by the analysis step.",
|
||||
"Could not get category input to github/codeql-action/analyze since there were multiple steps " +
|
||||
"calling github/codeql-action/analyze with different values for category.",
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
106
src/workflow.ts
106
src/workflow.ts
@@ -293,16 +293,71 @@ export function getWorkflowRunID(): number {
|
||||
return workflowRunID;
|
||||
}
|
||||
|
||||
export function getAnalyzeSteps(job: WorkflowJob): WorkflowJobStep[] {
|
||||
export function getStepsCallingAction(
|
||||
job: WorkflowJob,
|
||||
actionName: string
|
||||
): WorkflowJobStep[] {
|
||||
const steps = job.steps;
|
||||
if (!Array.isArray(steps)) {
|
||||
throw new Error(
|
||||
"Could not get analyze steps since job.steps was not an array."
|
||||
`Could not get steps calling ${actionName} since job.steps was not an array.`
|
||||
);
|
||||
}
|
||||
return steps.filter((step) =>
|
||||
step.uses?.includes("github/codeql-action/analyze")
|
||||
);
|
||||
return steps.filter((step) => step.uses?.includes(actionName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a best effort attempt to retrieve the value of a particular input with which
|
||||
* an Action in the workflow would be invoked.
|
||||
*
|
||||
* @returns the value of the input, or undefined if no such input is passed to the Action
|
||||
* @throws an error if the value of the input could not be determined, or we could not
|
||||
* determine that no such input is passed to the Action.
|
||||
*/
|
||||
function getInputOrThrow(
|
||||
workflow: Workflow,
|
||||
actionName: string,
|
||||
inputName: string,
|
||||
matrixVars: { [key: string]: string }
|
||||
) {
|
||||
if (!workflow.jobs) {
|
||||
throw new Error(
|
||||
`Could not get ${inputName} input to ${actionName} since the workflow has no jobs.`
|
||||
);
|
||||
}
|
||||
const inputs: string[] = Object.values(workflow.jobs)
|
||||
.map((job) =>
|
||||
getStepsCallingAction(job, actionName).map(
|
||||
(step) => step.with?.[inputName]
|
||||
)
|
||||
)
|
||||
.flat()
|
||||
.filter((input) => input !== undefined)
|
||||
.map((input) => input!);
|
||||
|
||||
if (inputs.length === 0) {
|
||||
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}.`
|
||||
);
|
||||
}
|
||||
|
||||
// Make a basic attempt to substitute matrix variables
|
||||
// First normalize by removing whitespace
|
||||
let input = inputs[0].replace(/\${{\s+/, "${{").replace(/\s+}}/, "}}");
|
||||
for (const [key, value] of Object.entries(matrixVars)) {
|
||||
input = input.replace(`\${{matrix.${key}}}`, value);
|
||||
}
|
||||
|
||||
if (input.includes("${{")) {
|
||||
throw new Error(
|
||||
`Could not get ${inputName} input to ${actionName} since it contained an unrecognized dynamic value.`
|
||||
);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -312,41 +367,14 @@ export function getAnalyzeSteps(job: WorkflowJob): WorkflowJobStep[] {
|
||||
* @returns the category input, or undefined if the category input is not defined
|
||||
* @throws an error if the category input could not be determined
|
||||
*/
|
||||
export function tryGetCategoryInput(
|
||||
export function getCategoryInputOrThrow(
|
||||
workflow: Workflow,
|
||||
matrixVars: { [key: string]: string }
|
||||
): string | undefined {
|
||||
if (!workflow.jobs) {
|
||||
throw new Error(
|
||||
"Could not get category input since workflow.jobs was undefined."
|
||||
);
|
||||
}
|
||||
const categories: string[] = Object.values(workflow.jobs)
|
||||
.map((job) => getAnalyzeSteps(job).map((step) => step.with?.category))
|
||||
.flat()
|
||||
.filter((category) => category !== undefined)
|
||||
.map((category) => category!);
|
||||
|
||||
if (categories.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
if (!categories.every((category) => category === categories[0])) {
|
||||
throw new Error(
|
||||
"Could not get category input since multiple categories were specified by the analysis step."
|
||||
);
|
||||
}
|
||||
|
||||
// Make a basic attempt to substitute matrix variables
|
||||
// First normalize by removing whitespace
|
||||
let category = categories[0].replace(/\${{\s+/, "${{").replace(/\s+}}/, "}}");
|
||||
for (const [key, value] of Object.entries(matrixVars)) {
|
||||
category = category.replace(`\${{matrix.${key}}}`, value);
|
||||
}
|
||||
|
||||
if (category.includes("${{")) {
|
||||
throw new Error(
|
||||
"Could not get category input since it contained an unrecognized dynamic value."
|
||||
);
|
||||
}
|
||||
return category;
|
||||
return getInputOrThrow(
|
||||
workflow,
|
||||
"github/codeql-action/analyze",
|
||||
"category",
|
||||
matrixVars
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user