diff --git a/Makefile b/Makefile index fb357da..9dcc321 100755 --- a/Makefile +++ b/Makefile @@ -42,7 +42,7 @@ RELEASE_OPTS ?= --commit-all export NODE_VERSION := $(shell node -v) export RUNNER_VERSION := $(CI_RUNNER_VERSION) -all: build run-terminal +all: build clean: ## clean up the target directory rm -rf node_modules @@ -74,16 +74,7 @@ todo: # build targets build: ## builds, bundles, and tests the application -build: build-fast - -build-cover: ## builds, bundles, and tests the application with code coverage -build-cover: configure node_modules - -build-fast: ## builds, bundles, and tests the application -build-fast: configure node_modules - -build-strict: ## builds, bundles, and tests the application with type checks and extra warnings (slow) -build-strict: configure node_modules +build: bundle bundle: node_modules $(NODE_BIN)/rollup --config $(CONFIG_PATH)/rollup.js diff --git a/src/index.ts b/src/index.ts index 2ac86b0..182c35b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,6 +12,7 @@ const CONFIG_ARGS_PATH = 'config-path'; const MAIN_ARGS: Options = { alias: { + 'count': ['c'], 'includeTag': ['t', 'tag'], 'mode': ['m'], }, @@ -25,10 +26,14 @@ const MAIN_ARGS: Options = { 'includeTag', 'rules', ], + boolean: [ + 'count', + ], count: ['v'], default: { [CONFIG_ARGS_NAME]: `.${VERSION_INFO.app.name}.yml`, [CONFIG_ARGS_PATH]: [], + 'count': false, 'excludeLevel': [], 'excludeName': [], 'excludeTag': [], @@ -69,24 +74,33 @@ export async function main(argv: Array): Promise { const activeRules = await resolveRules(rules, args.argv as any); // run rules - let status = STATUS_SUCCESS; + let errors = 0; switch (args.argv.mode) { case 'check': for (const rule of activeRules) { - if (checkRule(rule, data)) { + if (checkRule(rule, data, logger)) { logger.info({ rule }, 'passed rule'); } else { logger.warn({ rule }, 'failed rule'); - status = STATUS_ERROR; + ++errors; } } break; default: logger.error({ mode: args.argv.mode }, 'unsupported mode'); - status = STATUS_ERROR; + ++errors; } - return status; + if (errors > 0) { + logger.error({ errors }, 'some rules failed'); + if (args.argv.count) { + return errors; + } else { + return STATUS_ERROR; + } + } else { + return STATUS_SUCCESS; + } } main(process.argv).then((status) => process.exit(status)).catch((err) => { diff --git a/src/rule.ts b/src/rule.ts index fe55282..78b1966 100644 --- a/src/rule.ts +++ b/src/rule.ts @@ -2,7 +2,7 @@ import * as Ajv from 'ajv'; import { readFile } from 'fs'; import { JSONPath } from 'jsonpath-plus'; import { intersection } from 'lodash'; -import { LogLevel } from 'noicejs'; +import { LogLevel, Logger } from 'noicejs'; import { promisify } from 'util'; import { safeLoad } from 'js-yaml'; import { CONFIG_SCHEMA } from './config'; @@ -81,12 +81,21 @@ export async function resolveRules(rules: Array, selector: RuleSelector): return Array.from(activeRules); } -export function checkRule(rule: Rule, data: any): boolean { +export function checkRule(rule: Rule, data: any, logger: Logger): boolean { const ajv = new ((Ajv as any).default)() const schema = ajv.compile(rule.schema); const scopes = JSONPath({ json: data, path: rule.nodes.select, }); - return scopes.every((s: any) => schema(s)); + + for (const scope of scopes) { + const valid = schema(scope); + if (!valid) { + logger.warn({ errors: schema.errors, item: scope }, 'rule failed on item'); + return false; + } + } + + return true; } \ No newline at end of file