Store and check action version in Config

This commit is contained in:
Michael B. Gale
2025-09-10 12:43:56 +01:00
parent eb50a881d8
commit 4f56152a48
12 changed files with 171 additions and 10 deletions

View File

@@ -117960,7 +117960,18 @@ async function getConfig(tempDir, logger) {
const configString = fs3.readFileSync(configFile, "utf8");
logger.debug("Loaded config:");
logger.debug(configString);
return JSON.parse(configString);
const config = JSON.parse(configString);
if (config.version === void 0) {
throw new ConfigurationError(
`Loaded configuration file, but it does not contain the expected 'version' field.`
);
}
if (config.version !== getActionVersion()) {
throw new ConfigurationError(
`Loaded a configuration file for version '${config.version}', but running version '${getActionVersion()}'`
);
}
return config;
}
function appendExtraQueryExclusions(extraQueryExclusions, cliConfig) {
const augmentedConfig = cloneObject(cliConfig);

13
lib/analyze-action.js generated
View File

@@ -91620,7 +91620,18 @@ async function getConfig(tempDir, logger) {
const configString = fs9.readFileSync(configFile, "utf8");
logger.debug("Loaded config:");
logger.debug(configString);
return JSON.parse(configString);
const config = JSON.parse(configString);
if (config.version === void 0) {
throw new ConfigurationError(
`Loaded configuration file, but it does not contain the expected 'version' field.`
);
}
if (config.version !== getActionVersion()) {
throw new ConfigurationError(
`Loaded a configuration file for version '${config.version}', but running version '${getActionVersion()}'`
);
}
return config;
}
function appendExtraQueryExclusions(extraQueryExclusions, cliConfig) {
const augmentedConfig = cloneObject(cliConfig);

View File

@@ -78962,7 +78962,18 @@ async function getConfig(tempDir, logger) {
const configString = fs4.readFileSync(configFile, "utf8");
logger.debug("Loaded config:");
logger.debug(configString);
return JSON.parse(configString);
const config = JSON.parse(configString);
if (config.version === void 0) {
throw new ConfigurationError(
`Loaded configuration file, but it does not contain the expected 'version' field.`
);
}
if (config.version !== getActionVersion()) {
throw new ConfigurationError(
`Loaded a configuration file for version '${config.version}', but running version '${getActionVersion()}'`
);
}
return config;
}
function appendExtraQueryExclusions(extraQueryExclusions, cliConfig) {
const augmentedConfig = cloneObject(cliConfig);

View File

@@ -129575,7 +129575,18 @@ async function getConfig(tempDir, logger) {
const configString = fs9.readFileSync(configFile, "utf8");
logger.debug("Loaded config:");
logger.debug(configString);
return JSON.parse(configString);
const config = JSON.parse(configString);
if (config.version === void 0) {
throw new ConfigurationError(
`Loaded configuration file, but it does not contain the expected 'version' field.`
);
}
if (config.version !== getActionVersion()) {
throw new ConfigurationError(
`Loaded a configuration file for version '${config.version}', but running version '${getActionVersion()}'`
);
}
return config;
}
function appendExtraQueryExclusions(extraQueryExclusions, cliConfig) {
const augmentedConfig = cloneObject(cliConfig);

1
lib/init-action.js generated
View File

@@ -87335,6 +87335,7 @@ async function initActionState({
augmentationProperties
);
return {
version: getActionVersion(),
analysisKinds,
languages,
buildMode,

View File

@@ -78689,7 +78689,18 @@ async function getConfig(tempDir, logger) {
const configString = fs3.readFileSync(configFile, "utf8");
logger.debug("Loaded config:");
logger.debug(configString);
return JSON.parse(configString);
const config = JSON.parse(configString);
if (config.version === void 0) {
throw new ConfigurationError(
`Loaded configuration file, but it does not contain the expected 'version' field.`
);
}
if (config.version !== getActionVersion()) {
throw new ConfigurationError(
`Loaded a configuration file for version '${config.version}', but running version '${getActionVersion()}'`
);
}
return config;
}
function appendExtraQueryExclusions(extraQueryExclusions, cliConfig) {
const augmentedConfig = cloneObject(cliConfig);

View File

@@ -117369,7 +117369,18 @@ async function getConfig(tempDir, logger) {
const configString = fs.readFileSync(configFile, "utf8");
logger.debug("Loaded config:");
logger.debug(configString);
return JSON.parse(configString);
const config = JSON.parse(configString);
if (config.version === void 0) {
throw new ConfigurationError(
`Loaded configuration file, but it does not contain the expected 'version' field.`
);
}
if (config.version !== getActionVersion()) {
throw new ConfigurationError(
`Loaded a configuration file for version '${config.version}', but running version '${getActionVersion()}'`
);
}
return config;
}
// src/debug-artifacts.ts

13
lib/upload-lib.js generated
View File

@@ -89403,7 +89403,18 @@ async function getConfig(tempDir, logger) {
const configString = fs7.readFileSync(configFile, "utf8");
logger.debug("Loaded config:");
logger.debug(configString);
return JSON.parse(configString);
const config = JSON.parse(configString);
if (config.version === void 0) {
throw new ConfigurationError(
`Loaded configuration file, but it does not contain the expected 'version' field.`
);
}
if (config.version !== getActionVersion()) {
throw new ConfigurationError(
`Loaded a configuration file for version '${config.version}', but running version '${getActionVersion()}'`
);
}
return config;
}
function appendExtraQueryExclusions(extraQueryExclusions, cliConfig) {
const augmentedConfig = cloneObject(cliConfig);

View File

@@ -89677,7 +89677,18 @@ async function getConfig(tempDir, logger) {
const configString = fs8.readFileSync(configFile, "utf8");
logger.debug("Loaded config:");
logger.debug(configString);
return JSON.parse(configString);
const config = JSON.parse(configString);
if (config.version === void 0) {
throw new ConfigurationError(
`Loaded configuration file, but it does not contain the expected 'version' field.`
);
}
if (config.version !== getActionVersion()) {
throw new ConfigurationError(
`Loaded a configuration file for version '${config.version}', but running version '${getActionVersion()}'`
);
}
return config;
}
function appendExtraQueryExclusions(extraQueryExclusions, cliConfig) {
const augmentedConfig = cloneObject(cliConfig);

View File

@@ -199,6 +199,7 @@ test("load code quality config", async (t) => {
// And the config we expect it to result in
const expectedConfig: configUtils.Config = {
version: actionsUtil.getActionVersion(),
analysisKinds: [AnalysisKind.CodeQuality],
languages: [KnownLanguage.actions],
buildMode: undefined,
@@ -273,6 +274,55 @@ test("loading config saves config", async (t) => {
});
});
test("loading config with version mismatch throws", async (t) => {
return await withTmpDir(async (tempDir) => {
const logger = getRunnerLogger(true);
const codeql = createStubCodeQL({
async betterResolveLanguages() {
return {
extractors: {
javascript: [{ extractor_root: "" }],
python: [{ extractor_root: "" }],
},
};
},
});
// Sanity check the saved config file does not already exist
t.false(fs.existsSync(configUtils.getPathToParsedConfigFile(tempDir)));
// Sanity check that getConfig returns undefined before we have called initConfig
t.deepEqual(await configUtils.getConfig(tempDir, logger), undefined);
// Stub `getActionVersion` to return some nonsense.
const getActionVersionStub = sinon
.stub(actionsUtil, "getActionVersion")
.resolves("does-not-exist");
await configUtils.initConfig(
createTestInitConfigInputs({
languagesInput: "javascript,python",
tempDir,
codeql,
workspacePath: tempDir,
logger,
}),
);
// Restore `getActionVersion`.
getActionVersionStub.restore();
// The saved config file should now exist
t.true(fs.existsSync(configUtils.getPathToParsedConfigFile(tempDir)));
// Trying to read the configuration should now throw an error.
await t.throwsAsync(configUtils.getConfig(tempDir, logger), {
instanceOf: ConfigurationError,
});
});
});
test("load input outside of workspace", async (t) => {
return await withTmpDir(async (tempDir) => {
try {
@@ -389,6 +439,7 @@ test("load non-empty input", async (t) => {
// And the config we expect it to parse to
const expectedConfig: configUtils.Config = {
version: actionsUtil.getActionVersion(),
analysisKinds: [AnalysisKind.CodeScanning],
languages: [KnownLanguage.javascript],
buildMode: BuildMode.None,

View File

@@ -5,7 +5,7 @@ import { performance } from "perf_hooks";
import * as yaml from "js-yaml";
import * as semver from "semver";
import { isAnalyzingPullRequest } from "./actions-util";
import { getActionVersion, isAnalyzingPullRequest } from "./actions-util";
import {
AnalysisConfig,
AnalysisKind,
@@ -102,6 +102,10 @@ interface IncludeQueryFilter {
* Format of the parsed config file.
*/
export interface Config {
/**
* The version of the CodeQL Action that the configuration is for.
*/
version: string;
/**
* Set of analysis kinds that are enabled.
*/
@@ -591,6 +595,7 @@ export async function initActionState(
);
return {
version: getActionVersion(),
analysisKinds,
languages,
buildMode,
@@ -1308,7 +1313,21 @@ export async function getConfig(
const configString = fs.readFileSync(configFile, "utf8");
logger.debug("Loaded config:");
logger.debug(configString);
return JSON.parse(configString) as Config;
const config = JSON.parse(configString) as Partial<Config>;
if (config.version === undefined) {
throw new ConfigurationError(
`Loaded configuration file, but it does not contain the expected 'version' field.`,
);
}
if (config.version !== getActionVersion()) {
throw new ConfigurationError(
`Loaded a configuration file for version '${config.version}', but running version '${getActionVersion()}'`,
);
}
return config as Config;
}
/**

View File

@@ -6,6 +6,7 @@ import { TestFn } from "ava";
import nock from "nock";
import * as sinon from "sinon";
import { getActionVersion } from "./actions-util";
import { AnalysisKind } from "./analyses";
import * as apiClient from "./api-client";
import { GitHubApiDetails } from "./api-client";
@@ -356,6 +357,7 @@ export function createTestConfig(overrides: Partial<Config>): Config {
return Object.assign(
{},
{
version: getActionVersion(),
analysisKinds: [AnalysisKind.CodeScanning],
languages: [],
buildMode: undefined,