Generalize getCategoryInputOrThrow to arbitrary inputs
This commit is contained in:
parent
daf4614f68
commit
bff0be7364
6 changed files with 141 additions and 97 deletions
74
lib/workflow.js
generated
74
lib/workflow.js
generated
|
|
@ -19,7 +19,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.tryGetCategoryInput = exports.getAnalyzeSteps = exports.getWorkflowRunID = exports.getWorkflowPath = exports.getWorkflow = exports.formatWorkflowCause = exports.formatWorkflowErrors = exports.validateWorkflow = exports.getWorkflowErrors = exports.WorkflowErrors = exports.patternIsSuperset = void 0;
|
exports.getCategoryInputOrThrow = exports.getStepsCallingAction = exports.getWorkflowRunID = exports.getWorkflowPath = exports.getWorkflow = exports.formatWorkflowCause = exports.formatWorkflowErrors = exports.validateWorkflow = exports.getWorkflowErrors = exports.WorkflowErrors = exports.patternIsSuperset = void 0;
|
||||||
const fs = __importStar(require("fs"));
|
const fs = __importStar(require("fs"));
|
||||||
const path = __importStar(require("path"));
|
const path = __importStar(require("path"));
|
||||||
const core = __importStar(require("@actions/core"));
|
const core = __importStar(require("@actions/core"));
|
||||||
|
|
@ -246,14 +246,49 @@ function getWorkflowRunID() {
|
||||||
return workflowRunID;
|
return workflowRunID;
|
||||||
}
|
}
|
||||||
exports.getWorkflowRunID = getWorkflowRunID;
|
exports.getWorkflowRunID = getWorkflowRunID;
|
||||||
function getAnalyzeSteps(job) {
|
function getStepsCallingAction(job, actionName) {
|
||||||
const steps = job.steps;
|
const steps = job.steps;
|
||||||
if (!Array.isArray(steps)) {
|
if (!Array.isArray(steps)) {
|
||||||
throw new Error("Could not get analyze steps since job.steps was not an array.");
|
throw new Error(`Could not get steps calling ${actionName} since job.steps was not an array.`);
|
||||||
}
|
}
|
||||||
return steps.filter((step) => { var _a; return (_a = step.uses) === null || _a === void 0 ? void 0 : _a.includes("github/codeql-action/analyze"); });
|
return steps.filter((step) => { var _a; return (_a = step.uses) === null || _a === void 0 ? void 0 : _a.includes(actionName); });
|
||||||
|
}
|
||||||
|
exports.getStepsCallingAction = getStepsCallingAction;
|
||||||
|
/**
|
||||||
|
* 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, actionName, inputName, matrixVars) {
|
||||||
|
if (!workflow.jobs) {
|
||||||
|
throw new Error(`Could not get ${inputName} input to ${actionName} since the workflow has no jobs.`);
|
||||||
|
}
|
||||||
|
const inputs = Object.values(workflow.jobs)
|
||||||
|
.map((job) => getStepsCallingAction(job, actionName).map((step) => { var _a; return (_a = step.with) === null || _a === void 0 ? void 0 : _a[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;
|
||||||
}
|
}
|
||||||
exports.getAnalyzeSteps = getAnalyzeSteps;
|
|
||||||
/**
|
/**
|
||||||
* Makes a best effort attempt to retrieve the category input for the particular job,
|
* Makes a best effort attempt to retrieve the category input for the particular job,
|
||||||
* given a set of matrix variables.
|
* given a set of matrix variables.
|
||||||
|
|
@ -261,31 +296,8 @@ exports.getAnalyzeSteps = getAnalyzeSteps;
|
||||||
* @returns the category input, or undefined if the category input is not defined
|
* @returns the category input, or undefined if the category input is not defined
|
||||||
* @throws an error if the category input could not be determined
|
* @throws an error if the category input could not be determined
|
||||||
*/
|
*/
|
||||||
function tryGetCategoryInput(workflow, matrixVars) {
|
function getCategoryInputOrThrow(workflow, matrixVars) {
|
||||||
if (!workflow.jobs) {
|
return getInputOrThrow(workflow, "github/codeql-action/analyze", "category", matrixVars);
|
||||||
throw new Error("Could not get category input since workflow.jobs was undefined.");
|
|
||||||
}
|
|
||||||
const categories = Object.values(workflow.jobs)
|
|
||||||
.map((job) => getAnalyzeSteps(job).map((step) => { var _a; return (_a = step.with) === null || _a === void 0 ? void 0 : _a.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;
|
|
||||||
}
|
}
|
||||||
exports.tryGetCategoryInput = tryGetCategoryInput;
|
exports.getCategoryInputOrThrow = getCategoryInputOrThrow;
|
||||||
//# sourceMappingURL=workflow.js.map
|
//# sourceMappingURL=workflow.js.map
|
||||||
File diff suppressed because one or more lines are too long
26
lib/workflow.test.js
generated
26
lib/workflow.test.js
generated
|
|
@ -355,8 +355,8 @@ function errorCodes(actual, expected) {
|
||||||
on: ["push"]
|
on: ["push"]
|
||||||
`)), []));
|
`)), []));
|
||||||
});
|
});
|
||||||
(0, ava_1.default)("tryGetCategoryInput returns category for simple workflow with category", (t) => {
|
(0, ava_1.default)("getCategoryInputOrThrow returns category for simple workflow with category", (t) => {
|
||||||
t.is((0, workflow_1.tryGetCategoryInput)(yaml.load(`
|
t.is((0, workflow_1.getCategoryInputOrThrow)(yaml.load(`
|
||||||
jobs:
|
jobs:
|
||||||
analysis:
|
analysis:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
@ -368,8 +368,8 @@ function errorCodes(actual, expected) {
|
||||||
category: some-category
|
category: some-category
|
||||||
`), {}), "some-category");
|
`), {}), "some-category");
|
||||||
});
|
});
|
||||||
(0, ava_1.default)("tryGetCategoryInput returns undefined for simple workflow without category", (t) => {
|
(0, ava_1.default)("getCategoryInputOrThrow returns undefined for simple workflow without category", (t) => {
|
||||||
t.is((0, workflow_1.tryGetCategoryInput)(yaml.load(`
|
t.is((0, workflow_1.getCategoryInputOrThrow)(yaml.load(`
|
||||||
jobs:
|
jobs:
|
||||||
analysis:
|
analysis:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
@ -379,8 +379,8 @@ function errorCodes(actual, expected) {
|
||||||
- uses: github/codeql-action/analyze@v2
|
- uses: github/codeql-action/analyze@v2
|
||||||
`), {}), undefined);
|
`), {}), undefined);
|
||||||
});
|
});
|
||||||
(0, ava_1.default)("tryGetCategoryInput finds category for workflow with language matrix", (t) => {
|
(0, ava_1.default)("getCategoryInputOrThrow finds category for workflow with language matrix", (t) => {
|
||||||
t.is((0, workflow_1.tryGetCategoryInput)(yaml.load(`
|
t.is((0, workflow_1.getCategoryInputOrThrow)(yaml.load(`
|
||||||
jobs:
|
jobs:
|
||||||
analysis:
|
analysis:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
@ -397,8 +397,8 @@ function errorCodes(actual, expected) {
|
||||||
category: "/language:\${{ matrix.language }}"
|
category: "/language:\${{ matrix.language }}"
|
||||||
`), { language: "javascript" }), "/language:javascript");
|
`), { language: "javascript" }), "/language:javascript");
|
||||||
});
|
});
|
||||||
(0, ava_1.default)("tryGetCategoryInput throws error for workflow with dynamic category", (t) => {
|
(0, ava_1.default)("getCategoryInputOrThrow throws error for workflow with dynamic category", (t) => {
|
||||||
t.throws(() => (0, workflow_1.tryGetCategoryInput)(yaml.load(`
|
t.throws(() => (0, workflow_1.getCategoryInputOrThrow)(yaml.load(`
|
||||||
jobs:
|
jobs:
|
||||||
analysis:
|
analysis:
|
||||||
steps:
|
steps:
|
||||||
|
|
@ -408,11 +408,12 @@ function errorCodes(actual, expected) {
|
||||||
with:
|
with:
|
||||||
category: "\${{ github.workflow }}"
|
category: "\${{ github.workflow }}"
|
||||||
`), {}), {
|
`), {}), {
|
||||||
message: "Could not get category input since it contained an unrecognized dynamic value.",
|
message: "Could not get category input to github/codeql-action/analyze since it contained " +
|
||||||
|
"an unrecognized dynamic value.",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
(0, ava_1.default)("tryGetCategoryInput throws error for workflow with multiple categories", (t) => {
|
(0, ava_1.default)("getCategoryInputOrThrow throws error for workflow with multiple categories", (t) => {
|
||||||
t.throws(() => (0, workflow_1.tryGetCategoryInput)(yaml.load(`
|
t.throws(() => (0, workflow_1.getCategoryInputOrThrow)(yaml.load(`
|
||||||
jobs:
|
jobs:
|
||||||
analysis:
|
analysis:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
@ -426,7 +427,8 @@ function errorCodes(actual, expected) {
|
||||||
with:
|
with:
|
||||||
category: another-category
|
category: another-category
|
||||||
`), {}), {
|
`), {}), {
|
||||||
message: "Could not get category input since multiple categories were specified by the analysis step.",
|
message: "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.",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
//# sourceMappingURL=workflow.test.js.map
|
//# sourceMappingURL=workflow.test.js.map
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -6,7 +6,7 @@ import {
|
||||||
CodedError,
|
CodedError,
|
||||||
formatWorkflowCause,
|
formatWorkflowCause,
|
||||||
formatWorkflowErrors,
|
formatWorkflowErrors,
|
||||||
tryGetCategoryInput,
|
getCategoryInputOrThrow,
|
||||||
getWorkflowErrors,
|
getWorkflowErrors,
|
||||||
patternIsSuperset,
|
patternIsSuperset,
|
||||||
Workflow,
|
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(
|
t.is(
|
||||||
tryGetCategoryInput(
|
getCategoryInputOrThrow(
|
||||||
yaml.load(`
|
yaml.load(`
|
||||||
jobs:
|
jobs:
|
||||||
analysis:
|
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(
|
t.is(
|
||||||
tryGetCategoryInput(
|
getCategoryInputOrThrow(
|
||||||
yaml.load(`
|
yaml.load(`
|
||||||
jobs:
|
jobs:
|
||||||
analysis:
|
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(
|
t.is(
|
||||||
tryGetCategoryInput(
|
getCategoryInputOrThrow(
|
||||||
yaml.load(`
|
yaml.load(`
|
||||||
jobs:
|
jobs:
|
||||||
analysis:
|
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(
|
t.throws(
|
||||||
() =>
|
() =>
|
||||||
tryGetCategoryInput(
|
getCategoryInputOrThrow(
|
||||||
yaml.load(`
|
yaml.load(`
|
||||||
jobs:
|
jobs:
|
||||||
analysis:
|
analysis:
|
||||||
|
|
@ -605,15 +605,16 @@ test("tryGetCategoryInput throws error for workflow with dynamic category", (t)
|
||||||
),
|
),
|
||||||
{
|
{
|
||||||
message:
|
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(
|
t.throws(
|
||||||
() =>
|
() =>
|
||||||
tryGetCategoryInput(
|
getCategoryInputOrThrow(
|
||||||
yaml.load(`
|
yaml.load(`
|
||||||
jobs:
|
jobs:
|
||||||
analysis:
|
analysis:
|
||||||
|
|
@ -632,7 +633,8 @@ test("tryGetCategoryInput throws error for workflow with multiple categories", (
|
||||||
),
|
),
|
||||||
{
|
{
|
||||||
message:
|
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;
|
return workflowRunID;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getAnalyzeSteps(job: WorkflowJob): WorkflowJobStep[] {
|
export function getStepsCallingAction(
|
||||||
|
job: WorkflowJob,
|
||||||
|
actionName: string
|
||||||
|
): WorkflowJobStep[] {
|
||||||
const steps = job.steps;
|
const steps = job.steps;
|
||||||
if (!Array.isArray(steps)) {
|
if (!Array.isArray(steps)) {
|
||||||
throw new Error(
|
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) =>
|
return steps.filter((step) => step.uses?.includes(actionName));
|
||||||
step.uses?.includes("github/codeql-action/analyze")
|
}
|
||||||
);
|
|
||||||
|
/**
|
||||||
|
* 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
|
* @returns the category input, or undefined if the category input is not defined
|
||||||
* @throws an error if the category input could not be determined
|
* @throws an error if the category input could not be determined
|
||||||
*/
|
*/
|
||||||
export function tryGetCategoryInput(
|
export function getCategoryInputOrThrow(
|
||||||
workflow: Workflow,
|
workflow: Workflow,
|
||||||
matrixVars: { [key: string]: string }
|
matrixVars: { [key: string]: string }
|
||||||
): string | undefined {
|
): string | undefined {
|
||||||
if (!workflow.jobs) {
|
return getInputOrThrow(
|
||||||
throw new Error(
|
workflow,
|
||||||
"Could not get category input since workflow.jobs was undefined."
|
"github/codeql-action/analyze",
|
||||||
);
|
"category",
|
||||||
}
|
matrixVars
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue