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 \
|
--config-name config-stderr.yml \
|
||||||
--rules $(ROOT_PATH)/rules/salty-dog.yml \
|
--rules $(ROOT_PATH)/rules/salty-dog.yml \
|
||||||
--source $${file} \
|
--source $${file} \
|
||||||
--tag important > /dev/null; \
|
--tag important > /dev/null || exit 1; \
|
||||||
done
|
done
|
||||||
|
|
||||||
run-stream: ## validate stdin and write it to stdout, errors to stderr
|
run-stream: ## validate stdin and write it to stdout, errors to stderr
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
name: salty-dog-ansible
|
||||||
rules:
|
rules:
|
||||||
- name: ansible-playbook
|
- name: ansible-playbook
|
||||||
desc: ensure plays have important properties
|
desc: ensure plays have important properties
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
name: salty-dog-gitlab-ci
|
||||||
rules:
|
rules:
|
||||||
- name: gitlab-stages
|
- name: gitlab-stages
|
||||||
desc: should specify stages
|
desc: should specify stages
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
name: salty-dog-kubernetes
|
||||||
rules:
|
rules:
|
||||||
- name: kubernetes-resources
|
- name: kubernetes-resources
|
||||||
desc: containers must have complete resources specified
|
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:
|
rules:
|
||||||
- name: salty-dog-rule
|
- name: salty-dog-rule
|
||||||
desc: rules must be complete
|
desc: rules must be complete
|
||||||
|
@ -7,42 +49,32 @@ rules:
|
||||||
- salty-dog
|
- salty-dog
|
||||||
|
|
||||||
select: '$.rules[*]'
|
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:
|
check:
|
||||||
type: object
|
type: object
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
required:
|
required: [name, rules]
|
||||||
# metadata
|
|
||||||
- name
|
|
||||||
- desc
|
|
||||||
- level
|
|
||||||
- tags
|
|
||||||
# data
|
|
||||||
- select
|
|
||||||
- check
|
|
||||||
properties:
|
properties:
|
||||||
|
definitions:
|
||||||
|
type: object
|
||||||
|
additionalProperties: false
|
||||||
|
patternProperties:
|
||||||
|
"[-a-z]+":
|
||||||
|
type: object
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
pattern: "[-a-z0-9]+"
|
rules:
|
||||||
desc:
|
|
||||||
type: string
|
|
||||||
minLength: 8
|
|
||||||
maxLength: 255
|
|
||||||
level:
|
|
||||||
type: string
|
|
||||||
enum:
|
|
||||||
- debug
|
|
||||||
- info
|
|
||||||
- warn
|
|
||||||
- error
|
|
||||||
tags:
|
|
||||||
type: array
|
type: array
|
||||||
|
minItems: 1
|
||||||
items:
|
items:
|
||||||
type: string
|
$ref: "salty-dog-meta#/definitions/rule"
|
||||||
pattern: "[-:a-z0-9]+"
|
|
||||||
select:
|
|
||||||
type: string
|
|
||||||
minLength: 1
|
|
||||||
filter:
|
|
||||||
type: object
|
|
||||||
check:
|
|
||||||
type: object
|
|
|
@ -103,10 +103,6 @@ export async function main(argv: Array<string>): Promise<number> {
|
||||||
return STATUS_ERROR;
|
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({
|
const ctx = new VisitorContext({
|
||||||
coerce: args.coerce,
|
coerce: args.coerce,
|
||||||
defaults: args.mode === 'fix',
|
defaults: args.mode === 'fix',
|
||||||
|
@ -114,8 +110,12 @@ export async function main(argv: Array<string>): Promise<number> {
|
||||||
});
|
});
|
||||||
|
|
||||||
const parser = new YamlParser();
|
const parser = new YamlParser();
|
||||||
|
const source = await loadSource(args.source);
|
||||||
let data = parser.parse(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) {
|
for (const rule of activeRules) {
|
||||||
const workingCopy = cloneDeep(data);
|
const workingCopy = cloneDeep(data);
|
||||||
const ruleErrors = await rule.visit(ctx, workingCopy);
|
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>;
|
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 parser = new YamlParser();
|
||||||
const rules = [];
|
const rules = [];
|
||||||
|
|
||||||
|
@ -37,7 +43,15 @@ export async function loadRules(paths: Array<string>): Promise<Array<Rule>> {
|
||||||
encoding: 'utf-8',
|
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)));
|
rules.push(...data.rules.map((data: any) => new Rule(data)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue