Enable strict checking of config during CLI.
Signed-off-by: Aramis Sennyey <sennyeya@amazon.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/cli': patch
|
||||
---
|
||||
|
||||
Enable strict config checking during `backstage-cli config:check` with the new `--strict` option which will surface schema errors.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/config-loader': patch
|
||||
---
|
||||
|
||||
Added a new `noUndeclaredProperties` option to `SchemaLoader` to support enforcing that there are no extra keys when verifying config.
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-user-settings': patch
|
||||
'@backstage/plugin-auth-backend': patch
|
||||
---
|
||||
|
||||
Fix config schema definition.
|
||||
@@ -290,7 +290,8 @@ Options:
|
||||
--package <name> Only load config schema that applies to the given package
|
||||
--lax Do not require environment variables to be set
|
||||
--frontend Only validate the frontend configuration
|
||||
--deprecated List all deprecated configuration settings
|
||||
--deprecated Output deprecated configuration settings
|
||||
--strict Ensure that the provided config(s) has no errors and does not contain keys not in the schema.
|
||||
--config <path> Config files to load instead of app-config.yaml (default: [])
|
||||
-h, --help display help for command
|
||||
```
|
||||
|
||||
@@ -58,6 +58,7 @@ Options:
|
||||
--lax
|
||||
--frontend
|
||||
--deprecated
|
||||
--strict
|
||||
--config <path>
|
||||
-h, --help
|
||||
```
|
||||
|
||||
@@ -24,5 +24,6 @@ export default async (opts: OptionValues) => {
|
||||
mockEnv: opts.lax,
|
||||
fullVisibility: !opts.frontend,
|
||||
withDeprecatedKeys: opts.deprecated,
|
||||
strict: opts.strict,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -345,6 +345,10 @@ export function registerCommands(program: Command) {
|
||||
.option('--lax', 'Do not require environment variables to be set')
|
||||
.option('--frontend', 'Only validate the frontend configuration')
|
||||
.option('--deprecated', 'Output deprecated configuration settings')
|
||||
.option(
|
||||
'--strict',
|
||||
'Enable strict config validation, forbidding errors and unknown errors',
|
||||
)
|
||||
.option(...configOption)
|
||||
.description(
|
||||
'Validate that the given configuration loads and matches schema',
|
||||
|
||||
@@ -32,6 +32,7 @@ type Options = {
|
||||
withFilteredKeys?: boolean;
|
||||
withDeprecatedKeys?: boolean;
|
||||
fullVisibility?: boolean;
|
||||
strict?: boolean;
|
||||
};
|
||||
|
||||
export async function loadCliConfig(options: Options) {
|
||||
@@ -70,6 +71,7 @@ export async function loadCliConfig(options: Options) {
|
||||
dependencies: localPackageNames,
|
||||
// Include the package.json in the project root if it exists
|
||||
packagePaths: [paths.resolveTargetRoot('package.json')],
|
||||
noUndeclaredProperties: options.strict,
|
||||
});
|
||||
|
||||
const { appConfigs } = await loadConfig({
|
||||
@@ -93,6 +95,7 @@ export async function loadCliConfig(options: Options) {
|
||||
: ['frontend'],
|
||||
withFilteredKeys: options.withFilteredKeys,
|
||||
withDeprecatedKeys: options.withDeprecatedKeys,
|
||||
ignoreSchemaErrors: !options.strict,
|
||||
});
|
||||
const frontendConfig = ConfigReader.fromConfigs(frontendAppConfigs);
|
||||
|
||||
|
||||
@@ -180,14 +180,17 @@ export function loadConfigSchema(
|
||||
): Promise<ConfigSchema>;
|
||||
|
||||
// @public
|
||||
export type LoadConfigSchemaOptions =
|
||||
export type LoadConfigSchemaOptions = (
|
||||
| {
|
||||
dependencies: string[];
|
||||
packagePaths?: string[];
|
||||
}
|
||||
| {
|
||||
serialized: JsonObject;
|
||||
};
|
||||
}
|
||||
) & {
|
||||
noUndeclaredProperties?: boolean;
|
||||
};
|
||||
|
||||
// @public
|
||||
export function mergeConfigSchemas(schemas: JSONSchema7[]): JSONSchema7;
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
CONFIG_VISIBILITIES,
|
||||
ConfigVisibility,
|
||||
} from './types';
|
||||
import { SchemaObject } from 'json-schema-traverse';
|
||||
|
||||
/**
|
||||
* This takes a collection of Backstage configuration schemas from various
|
||||
@@ -35,6 +36,9 @@ import {
|
||||
*/
|
||||
export function compileConfigSchemas(
|
||||
schemas: ConfigSchemaPackageEntry[],
|
||||
options?: {
|
||||
noUndeclaredProperties?: boolean;
|
||||
},
|
||||
): ValidationFunc {
|
||||
// The ajv instance below is stateful and doesn't really allow for additional
|
||||
// output during validation. We work around this by having this extra piece
|
||||
@@ -102,6 +106,18 @@ export function compileConfigSchemas(
|
||||
|
||||
const merged = mergeConfigSchemas(schemas.map(_ => _.value));
|
||||
|
||||
if (options?.noUndeclaredProperties) {
|
||||
traverse(merged, (schema: SchemaObject) => {
|
||||
/**
|
||||
* The `additionalProperties` key can only be applied to `type: object` in the JSON
|
||||
* schema.
|
||||
*/
|
||||
if (schema?.type === 'object') {
|
||||
schema.additionalProperties ||= false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const validate = ajv.compile(merged);
|
||||
|
||||
const visibilityBySchemaPath = new Map<string, ConfigVisibility>();
|
||||
|
||||
@@ -32,12 +32,16 @@ import {
|
||||
* @public
|
||||
*/
|
||||
export type LoadConfigSchemaOptions =
|
||||
| {
|
||||
dependencies: string[];
|
||||
packagePaths?: string[];
|
||||
}
|
||||
| {
|
||||
serialized: JsonObject;
|
||||
| (
|
||||
| {
|
||||
dependencies: string[];
|
||||
packagePaths?: string[];
|
||||
}
|
||||
| {
|
||||
serialized: JsonObject;
|
||||
}
|
||||
) & {
|
||||
noUndeclaredProperties?: boolean;
|
||||
};
|
||||
|
||||
function errorsToError(errors: ValidationError[]): Error {
|
||||
@@ -77,7 +81,9 @@ export async function loadConfigSchema(
|
||||
schemas = serialized.schemas as ConfigSchemaPackageEntry[];
|
||||
}
|
||||
|
||||
const validate = compileConfigSchemas(schemas);
|
||||
const validate = compileConfigSchemas(schemas, {
|
||||
noUndeclaredProperties: options.noUndeclaredProperties,
|
||||
});
|
||||
|
||||
return {
|
||||
process(
|
||||
|
||||
Vendored
+1
@@ -59,6 +59,7 @@ export interface Config {
|
||||
|
||||
/**
|
||||
* The available auth-provider options and attributes
|
||||
* @additionalProperties true
|
||||
*/
|
||||
providers?: {
|
||||
google?: {
|
||||
|
||||
@@ -78,7 +78,8 @@
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"visibility": "frontend"
|
||||
"visibility": "frontend",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user