feat: load definitions from rules (fixes #2)
BREAKING CHANGE: adds a required `name` property (string) at the top level of each `--rules` file, used as the schema name
This commit is contained in:
parent
e02cd67164
commit
9eb41fc38e
2
Makefile
2
Makefile
|
@ -123,7 +123,7 @@ run-rules: ## validate the rules directory
|
|||
--config-name config-stderr.yml \
|
||||
--rules $(ROOT_PATH)/rules/salty-dog.yml \
|
||||
--source $${file} \
|
||||
--tag important > /dev/null; \
|
||||
--tag important > /dev/null || exit 1; \
|
||||
done
|
||||
|
||||
run-stream: ## validate stdin and write it to stdout, errors to stderr
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
name: salty-dog-ansible
|
||||
rules:
|
||||
- name: ansible-playbook
|
||||
desc: ensure plays have important properties
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
name: salty-dog-gitlab-ci
|
||||
rules:
|
||||
- name: gitlab-stages
|
||||
desc: should specify stages
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
name: salty-dog-kubernetes
|
||||
rules:
|
||||
- name: kubernetes-resources
|
||||
desc: containers must have complete resources specified
|
||||
|
|
|
@ -1,3 +1,45 @@
|
|||
name: salty-dog-meta
|
||||
definitions:
|
||||
rule:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required:
|
||||
# metadata
|
||||
- name
|
||||
- desc
|
||||
- level
|
||||
- tags
|
||||
# data
|
||||
- select
|
||||
- check
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
pattern: "[-a-z0-9]+"
|
||||
desc:
|
||||
type: string
|
||||
minLength: 8
|
||||
maxLength: 255
|
||||
level:
|
||||
type: string
|
||||
enum:
|
||||
- debug
|
||||
- info
|
||||
- warn
|
||||
- error
|
||||
tags:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
pattern: "[-:a-z0-9]+"
|
||||
select:
|
||||
type: string
|
||||
minLength: 1
|
||||
filter:
|
||||
type: object
|
||||
check:
|
||||
type: object
|
||||
|
||||
rules:
|
||||
- name: salty-dog-rule
|
||||
desc: rules must be complete
|
||||
|
@ -7,42 +49,32 @@ rules:
|
|||
- salty-dog
|
||||
|
||||
select: '$.rules[*]'
|
||||
check:
|
||||
$ref: "salty-dog-meta#/definitions/rule"
|
||||
|
||||
- name: salty-dog-source
|
||||
desc: source files must have rules
|
||||
level: info
|
||||
tags:
|
||||
- important
|
||||
- salty-dog
|
||||
|
||||
select: '$'
|
||||
check:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required:
|
||||
# metadata
|
||||
- name
|
||||
- desc
|
||||
- level
|
||||
- tags
|
||||
# data
|
||||
- select
|
||||
- check
|
||||
required: [name, rules]
|
||||
properties:
|
||||
definitions:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
patternProperties:
|
||||
"[-a-z]+":
|
||||
type: object
|
||||
name:
|
||||
type: string
|
||||
pattern: "[-a-z0-9]+"
|
||||
desc:
|
||||
type: string
|
||||
minLength: 8
|
||||
maxLength: 255
|
||||
level:
|
||||
type: string
|
||||
enum:
|
||||
- debug
|
||||
- info
|
||||
- warn
|
||||
- error
|
||||
tags:
|
||||
rules:
|
||||
type: array
|
||||
minItems: 1
|
||||
items:
|
||||
type: string
|
||||
pattern: "[-:a-z0-9]+"
|
||||
select:
|
||||
type: string
|
||||
minLength: 1
|
||||
filter:
|
||||
type: object
|
||||
check:
|
||||
type: object
|
||||
$ref: "salty-dog-meta#/definitions/rule"
|
|
@ -103,10 +103,6 @@ export async function main(argv: Array<string>): Promise<number> {
|
|||
return STATUS_ERROR;
|
||||
}
|
||||
|
||||
const rules = await loadRules(args.rules);
|
||||
const source = await loadSource(args.source);
|
||||
|
||||
const activeRules = await resolveRules(rules, args as any);
|
||||
const ctx = new VisitorContext({
|
||||
coerce: args.coerce,
|
||||
defaults: args.mode === 'fix',
|
||||
|
@ -114,8 +110,12 @@ export async function main(argv: Array<string>): Promise<number> {
|
|||
});
|
||||
|
||||
const parser = new YamlParser();
|
||||
const source = await loadSource(args.source);
|
||||
let data = parser.parse(source);
|
||||
|
||||
const rules = await loadRules(args.rules, ctx.ajv);
|
||||
const activeRules = await resolveRules(rules, args as any);
|
||||
|
||||
for (const rule of activeRules) {
|
||||
const workingCopy = cloneDeep(data);
|
||||
const ruleErrors = await rule.visit(ctx, workingCopy);
|
||||
|
|
18
src/rule.ts
18
src/rule.ts
|
@ -28,7 +28,13 @@ export interface RuleSelector {
|
|||
includeTag: Array<string>;
|
||||
}
|
||||
|
||||
export async function loadRules(paths: Array<string>): Promise<Array<Rule>> {
|
||||
export interface RuleSource {
|
||||
definitions?: Array<any>;
|
||||
name: string;
|
||||
rules: Array<RuleData>;
|
||||
}
|
||||
|
||||
export async function loadRules(paths: Array<string>, ajv: any): Promise<Array<Rule>> {
|
||||
const parser = new YamlParser();
|
||||
const rules = [];
|
||||
|
||||
|
@ -37,7 +43,15 @@ export async function loadRules(paths: Array<string>): Promise<Array<Rule>> {
|
|||
encoding: 'utf-8',
|
||||
});
|
||||
|
||||
const data = parser.parse(contents);
|
||||
const data = parser.parse(contents) as RuleSource;
|
||||
|
||||
if (!isNil(data.definitions)) {
|
||||
ajv.addSchema({
|
||||
'$id': data.name,
|
||||
definitions: data.definitions,
|
||||
});
|
||||
}
|
||||
|
||||
rules.push(...data.rules.map((data: any) => new Rule(data)));
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue