Enforce that only compatible kinds can be enabled concurrently

This commit is contained in:
Michael B. Gale
2026-02-11 20:14:37 +00:00
parent 9267d8d51e
commit 5b3261bcbf
3 changed files with 80 additions and 6 deletions
+34
View File
@@ -4,6 +4,7 @@ import * as sinon from "sinon";
import * as actionsUtil from "./actions-util";
import {
AnalysisKind,
compatibilityMatrix,
getAnalysisKinds,
parseAnalysisKinds,
supportedAnalysisKinds,
@@ -67,3 +68,36 @@ test("getAnalysisKinds - throws if `analysis-kinds` input is invalid", async (t)
requiredInputStub.withArgs("analysis-kinds").returns("no-such-thing");
await t.throwsAsync(getAnalysisKinds(getRunnerLogger(true), true));
});
// Test the compatibility matrix by looping through all analysis kinds.
const analysisKinds = Object.values(AnalysisKind);
for (let i = 0; i < analysisKinds.length; i++) {
const analysisKind = analysisKinds[i];
for (let j = i + 1; j < analysisKinds.length; j++) {
const otherAnalysis = analysisKinds[j];
if (analysisKind === otherAnalysis) continue;
if (compatibilityMatrix[analysisKind].has(otherAnalysis)) {
test(`getAnalysisKinds - allows ${analysisKind} with ${otherAnalysis}`, async (t) => {
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
requiredInputStub
.withArgs("analysis-kinds")
.returns([analysisKind, otherAnalysis].join(","));
const result = await getAnalysisKinds(getRunnerLogger(true), true);
t.is(result.length, 2);
});
} else {
test(`getAnalysisKinds - throws if ${analysisKind} is enabled with ${otherAnalysis}`, async (t) => {
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
requiredInputStub
.withArgs("analysis-kinds")
.returns([analysisKind, otherAnalysis].join(","));
await t.throwsAsync(getAnalysisKinds(getRunnerLogger(true), true), {
instanceOf: ConfigurationError,
message: `${otherAnalysis} cannot be enabled at the same time as ${analysisKind}`,
});
});
}
}
}
+27 -3
View File
@@ -12,6 +12,15 @@ export enum AnalysisKind {
CSRA = "csra",
}
export type CompatibilityMatrix = Record<AnalysisKind, Set<AnalysisKind>>;
/** A mapping from analysis kinds to other analysis kinds which can be enabled concurrently. */
export const compatibilityMatrix: CompatibilityMatrix = {
[AnalysisKind.CodeScanning]: new Set([AnalysisKind.CodeQuality]),
[AnalysisKind.CodeQuality]: new Set([AnalysisKind.CodeScanning]),
[AnalysisKind.CSRA]: new Set(),
};
// Exported for testing. A set of all known analysis kinds.
export const supportedAnalysisKinds = new Set(Object.values(AnalysisKind));
@@ -68,7 +77,7 @@ export async function getAnalysisKinds(
return cachedAnalysisKinds;
}
cachedAnalysisKinds = await parseAnalysisKinds(
const analysisKinds = await parseAnalysisKinds(
getRequiredInput("analysis-kinds"),
);
@@ -86,12 +95,27 @@ export async function getAnalysisKinds(
// if an input to `quality-queries` was specified. We should remove this once
// `quality-queries` is no longer used.
if (
!cachedAnalysisKinds.includes(AnalysisKind.CodeQuality) &&
!analysisKinds.includes(AnalysisKind.CodeQuality) &&
qualityQueriesInput !== undefined
) {
cachedAnalysisKinds.push(AnalysisKind.CodeQuality);
analysisKinds.push(AnalysisKind.CodeQuality);
}
// Check that all enabled analysis kinds are compatible with each other.
for (const analysisKind of analysisKinds) {
for (const otherAnalysisKind of analysisKinds) {
if (analysisKind === otherAnalysisKind) continue;
if (!compatibilityMatrix[analysisKind].has(otherAnalysisKind)) {
throw new ConfigurationError(
`${otherAnalysisKind} cannot be enabled at the same time as ${analysisKind}`,
);
}
}
}
// Cache the analysis kinds and return them.
cachedAnalysisKinds = analysisKinds;
return cachedAnalysisKinds;
}