Substitute matrix variables into category input

This is a common case, so we should handle it.
This commit is contained in:
Henry Mercer 2022-11-22 18:26:14 +00:00
parent e2d523ca5e
commit daf4614f68
6 changed files with 119 additions and 53 deletions

View file

@ -6,7 +6,7 @@ import {
CodedError,
formatWorkflowCause,
formatWorkflowErrors,
getCategoryInput,
tryGetCategoryInput,
getWorkflowErrors,
patternIsSuperset,
Workflow,
@ -524,9 +524,9 @@ test("getWorkflowErrors() should not report an error if PRs are totally unconfig
);
});
test("getCategoryInput returns category for simple workflow with category", (t) => {
test("tryGetCategoryInput returns category for simple workflow with category", (t) => {
t.is(
getCategoryInput(
tryGetCategoryInput(
yaml.load(`
jobs:
analysis:
@ -537,15 +537,16 @@ test("getCategoryInput returns category for simple workflow with category", (t)
- uses: github/codeql-action/analyze@v2
with:
category: some-category
`) as Workflow
`) as Workflow,
{}
),
"some-category"
);
});
test("getCategoryInput returns undefined for simple workflow without category", (t) => {
test("tryGetCategoryInput returns undefined for simple workflow without category", (t) => {
t.is(
getCategoryInput(
tryGetCategoryInput(
yaml.load(`
jobs:
analysis:
@ -554,44 +555,65 @@ test("getCategoryInput returns undefined for simple workflow without category",
- uses: actions/checkout@v2
- uses: github/codeql-action/init@v2
- uses: github/codeql-action/analyze@v2
`) as Workflow
`) as Workflow,
{}
),
undefined
);
});
test("getCategoryInput throws error for workflow with dynamic category", (t) => {
test("tryGetCategoryInput finds category for workflow with language matrix", (t) => {
t.is(
tryGetCategoryInput(
yaml.load(`
jobs:
analysis:
runs-on: ubuntu-latest
strategy:
matrix:
language: [javascript, python]
steps:
- uses: actions/checkout@v2
- uses: github/codeql-action/init@v2
with:
language: \${{ matrix.language }}
- uses: github/codeql-action/analyze@v2
with:
category: "/language:\${{ matrix.language }}"
`) as Workflow,
{ language: "javascript" }
),
"/language:javascript"
);
});
test("tryGetCategoryInput throws error for workflow with dynamic category", (t) => {
t.throws(
() =>
getCategoryInput(
tryGetCategoryInput(
yaml.load(`
jobs:
analysis:
runs-on: ubuntu-latest
strategy:
matrix:
language: [javascript, python]
steps:
- uses: actions/checkout@v2
- uses: github/codeql-action/init@v2
with:
language: \${{ matrix.language }}
- uses: github/codeql-action/analyze@v2
with:
category: "/language:\${{ matrix.language }}"
`) as Workflow
category: "\${{ github.workflow }}"
`) as Workflow,
{}
),
{
message:
"Could not get category input since it contained a dynamic value.",
"Could not get category input since it contained an unrecognized dynamic value.",
}
);
});
test("getCategoryInput throws error for workflow with multiple categories", (t) => {
test("tryGetCategoryInput throws error for workflow with multiple categories", (t) => {
t.throws(
() =>
getCategoryInput(
tryGetCategoryInput(
yaml.load(`
jobs:
analysis:
@ -605,7 +627,8 @@ test("getCategoryInput throws error for workflow with multiple categories", (t)
- uses: github/codeql-action/analyze@v2
with:
category: another-category
`) as Workflow
`) as Workflow,
{}
),
{
message:

View file

@ -305,7 +305,17 @@ export function getAnalyzeSteps(job: WorkflowJob): WorkflowJobStep[] {
);
}
export function getCategoryInput(workflow: Workflow): string | undefined {
/**
* Makes a best effort attempt to retrieve the category input for the particular job,
* given a set of matrix variables.
*
* @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(
workflow: Workflow,
matrixVars: { [key: string]: string }
): string | undefined {
if (!workflow.jobs) {
throw new Error(
"Could not get category input since workflow.jobs was undefined."
@ -325,10 +335,18 @@ export function getCategoryInput(workflow: Workflow): string | undefined {
"Could not get category input since multiple categories were specified by the analysis step."
);
}
if (categories[0].includes("${{")) {
// 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 a dynamic value."
"Could not get category input since it contained an unrecognized dynamic value."
);
}
return categories[0];
return category;
}