diff --git a/lib/init-action.js b/lib/init-action.js index 0cfb86415..3b79532d4 100644 --- a/lib/init-action.js +++ b/lib/init-action.js @@ -85319,6 +85319,7 @@ function renamed(from, to) { var load = loader.load; var loadAll = loader.loadAll; var dump = dumper.dump; +var YAMLException = exception; var safeLoad = renamed("safeLoad", "load"); var safeLoadAll = renamed("safeLoadAll", "loadAll"); var safeDump = renamed("safeDump", "dump"); @@ -86310,6 +86311,9 @@ function getConfigFileOutsideWorkspaceErrorMessage(configFile) { function getConfigFileDoesNotExistErrorMessage(configFile) { return `The configuration file "${configFile}" does not exist`; } +function getConfigFileParseErrorMessage(configFile, message) { + return `Cannot parse "${configFile}": ${message}`; +} function getConfigFileRepoFormatInvalidMessage(configFile) { let error2 = `The configuration file "${configFile}" is not a supported remote file reference.`; error2 += " Expected format //@"; @@ -86601,6 +86605,18 @@ function generateCodeScanningConfig(logger, originalUserInput, augmentationPrope } return augmentedConfig; } +function parseUserConfig(pathInput, contents) { + try { + return load(contents); + } catch (error2) { + if (error2 instanceof YAMLException) { + throw new ConfigurationError( + getConfigFileParseErrorMessage(pathInput, error2.message) + ); + } + throw error2; + } +} // src/feature-flags.ts var fs7 = __toESM(require("fs")); @@ -88033,7 +88049,7 @@ function getLocalConfig(configFile) { getConfigFileDoesNotExistErrorMessage(configFile) ); } - return load(fs9.readFileSync(configFile, "utf8")); + return parseUserConfig(configFile, fs9.readFileSync(configFile, "utf-8")); } async function getRemoteConfig(configFile, apiDetails) { const format = new RegExp( @@ -88063,7 +88079,8 @@ async function getRemoteConfig(configFile, apiDetails) { getConfigFileFormatInvalidMessage(configFile) ); } - return load( + return parseUserConfig( + configFile, Buffer.from(fileContents, "base64").toString("binary") ); } diff --git a/src/config-utils.ts b/src/config-utils.ts index 25d7a949e..1c0346340 100644 --- a/src/config-utils.ts +++ b/src/config-utils.ts @@ -19,6 +19,7 @@ import { calculateAugmentation, ExcludeQueryFilter, generateCodeScanningConfig, + parseUserConfig, UserConfig, } from "./config/db-config"; import { shouldPerformDiffInformedAnalysis } from "./diff-informed-analysis-utils"; @@ -905,7 +906,7 @@ function getLocalConfig(configFile: string): UserConfig { ); } - return yaml.load(fs.readFileSync(configFile, "utf8")) as UserConfig; + return parseUserConfig(configFile, fs.readFileSync(configFile, "utf-8")); } async function getRemoteConfig( @@ -946,9 +947,10 @@ async function getRemoteConfig( ); } - return yaml.load( + return parseUserConfig( + configFile, Buffer.from(fileContents, "base64").toString("binary"), - ) as UserConfig; + ); } /** diff --git a/src/config/db-config.test.ts b/src/config/db-config.test.ts index 533459de2..b402b7025 100644 --- a/src/config/db-config.test.ts +++ b/src/config/db-config.test.ts @@ -2,7 +2,7 @@ import test, { ExecutionContext } from "ava"; import { RepositoryProperties } from "../feature-flags/properties"; import { KnownLanguage, Language } from "../languages"; -import { prettyPrintPack } from "../util"; +import { ConfigurationError, prettyPrintPack } from "../util"; import * as dbConfig from "./db-config"; @@ -391,3 +391,42 @@ test( {}, /"a-pack-without-a-scope" is not a valid pack/, ); + +test("parseUserConfig - successfully parses valid YAML", (t) => { + const result = dbConfig.parseUserConfig( + "test", + ` + paths-ignore: + - "some/path" + queries: + - uses: foo + `, + ); + t.truthy(result); + if (t.truthy(result["paths-ignore"])) { + t.is(result["paths-ignore"].length, 1); + t.is(result["paths-ignore"][0], "some/path"); + } + if (t.truthy(result["queries"])) { + t.is(result["queries"].length, 1); + t.deepEqual(result["queries"][0], { uses: "foo" }); + } +}); + +test("parseUserConfig - throws a ConfigurationError if the file is not valid YAML", (t) => { + t.throws( + () => + dbConfig.parseUserConfig( + "test", + ` + paths-ignore: + - "some/path" + queries: + - foo + `, + ), + { + instanceOf: ConfigurationError, + }, + ); +}); diff --git a/src/config/db-config.ts b/src/config/db-config.ts index 263949354..94a70a5e8 100644 --- a/src/config/db-config.ts +++ b/src/config/db-config.ts @@ -1,5 +1,6 @@ import * as path from "path"; +import * as yaml from "js-yaml"; import * as semver from "semver"; import * as errorMessages from "../error-messages"; @@ -474,3 +475,27 @@ export function generateCodeScanningConfig( return augmentedConfig; } + +/** + * Attempts to parse `contents` into a `UserConfig` value. + * + * @param pathInput The path to the file where `contents` was obtained from, for use in error messages. + * @param contents The string contents of a YAML file to try and parse as a `UserConfig`. + * @returns The `UserConfig` corresponding to `contents`, if parsing was successful. + * @throws A `ConfigurationError` if parsing failed. + */ +export function parseUserConfig( + pathInput: string, + contents: string, +): UserConfig { + try { + return yaml.load(contents) as UserConfig; + } catch (error) { + if (error instanceof yaml.YAMLException) { + throw new ConfigurationError( + errorMessages.getConfigFileParseErrorMessage(pathInput, error.message), + ); + } + throw error; + } +} diff --git a/src/error-messages.ts b/src/error-messages.ts index eb4926677..da464d040 100644 --- a/src/error-messages.ts +++ b/src/error-messages.ts @@ -14,6 +14,13 @@ export function getConfigFileDoesNotExistErrorMessage( return `The configuration file "${configFile}" does not exist`; } +export function getConfigFileParseErrorMessage( + configFile: string, + message: string, +): string { + return `Cannot parse "${configFile}": ${message}`; +} + export function getConfigFileRepoFormatInvalidMessage( configFile: string, ): string {