1
0
Fork 0
salty-dog/src/index.ts

149 lines
3.7 KiB
TypeScript
Raw Normal View History

2019-06-15 20:20:04 +00:00
import { createLogger } from 'bunyan';
2019-06-17 13:04:58 +00:00
import { Options, usage } from 'yargs';
2019-06-15 20:20:04 +00:00
import { loadConfig } from 'src/config';
import { YamlParser } from 'src/parser/YamlParser';
import { loadRules, resolveRules, visitRules } from 'src/rule';
2019-06-16 01:21:11 +00:00
import { loadSource, writeSource } from 'src/source';
2019-06-15 20:20:04 +00:00
import { VERSION_INFO } from 'src/version';
import { VisitorContext } from 'src/visitor/context';
2019-06-15 20:20:04 +00:00
const CONFIG_ARGS_NAME = 'config-name';
const CONFIG_ARGS_PATH = 'config-path';
2019-06-25 04:47:12 +00:00
const MODES = ['check', 'fix', 'list'];
2019-06-17 13:04:58 +00:00
const RULE_OPTION: Options = {
default: [],
2019-06-17 13:19:35 +00:00
group: 'Rules:',
2019-06-17 13:04:58 +00:00
type: 'array',
2019-06-15 20:20:04 +00:00
};
const MAIN_ARGS = usage(`Usage: salty-dog [-m mode] [options]`)
2019-06-17 13:04:58 +00:00
.option(CONFIG_ARGS_NAME, {
default: `.${VERSION_INFO.app.name}.yml`,
2019-06-17 13:19:35 +00:00
group: 'Config:',
2019-06-17 13:04:58 +00:00
type: 'string',
})
.option(CONFIG_ARGS_PATH, {
default: [],
2019-06-17 13:19:35 +00:00
group: 'Config:',
2019-06-17 13:04:58 +00:00
type: 'array',
})
.option('coerce', {
default: false,
type: 'boolean',
})
.option('count', {
alias: ['c'],
default: false,
2019-06-17 14:11:26 +00:00
desc: 'Exit with error count',
2019-06-17 13:04:58 +00:00
type: 'boolean',
})
.option('dest', {
alias: ['d'],
default: '-',
type: 'string',
})
.option('format', {
alias: ['f'],
default: 'yaml',
type: 'string',
})
.option('mode', {
alias: ['m'],
2019-06-25 04:47:12 +00:00
choices: MODES,
2019-06-17 13:04:58 +00:00
default: 'check',
type: 'string',
})
.option('rules', {
alias: ['r'],
default: [],
2019-06-17 14:11:26 +00:00
desc: 'Rules file',
2019-06-17 13:04:58 +00:00
type: 'array',
})
.option('source', {
alias: ['s'],
default: '-',
type: 'string',
})
.option('exclude-level', RULE_OPTION)
.option('exclude-name', RULE_OPTION)
.option('exclude-tag', RULE_OPTION)
.option('include-level', RULE_OPTION)
.option('include-name', RULE_OPTION)
.option('include-tag', {
2019-06-17 13:04:58 +00:00
...RULE_OPTION,
alias: ['t', 'tag'],
})
2019-06-25 04:47:12 +00:00
.help()
2019-07-01 01:09:38 +00:00
.version(VERSION_INFO.app.version)
.alias('version', 'v');
2019-06-17 13:04:58 +00:00
2019-06-15 20:20:04 +00:00
const STATUS_SUCCESS = 0;
const STATUS_ERROR = 1;
export async function main(argv: Array<string>): Promise<number> {
2019-06-17 13:04:58 +00:00
const args = MAIN_ARGS.argv;
const config = await loadConfig(args[CONFIG_ARGS_NAME], ...args[CONFIG_ARGS_PATH]);
2019-06-15 20:20:04 +00:00
const logger = createLogger(config.data.logger);
logger.info(VERSION_INFO, 'version info');
2019-06-17 13:04:58 +00:00
logger.info({ args }, 'main arguments');
2019-06-15 20:20:04 +00:00
// check mode
if (!MODES.includes(args.mode)) {
logger.error({ mode: args.mode }, 'unsupported mode');
return STATUS_ERROR;
}
2019-06-15 20:20:04 +00:00
// const schema = new Schema();
const result = { errors: [], valid: true }; // schema.match(config);
if (!result.valid) {
logger.error({ errors: result.errors }, 'config failed to validate');
return STATUS_ERROR;
}
2019-06-17 12:11:36 +00:00
const ctx = new VisitorContext({
2019-06-17 13:04:58 +00:00
coerce: args.coerce,
defaults: args.mode === 'fix',
2019-06-17 12:11:36 +00:00
logger,
});
const parser = new YamlParser();
const source = await loadSource(args.source);
let docs = parser.parse(source);
const rules = await loadRules(args.rules, ctx.ajv);
const activeRules = await resolveRules(rules, args as any);
2019-06-25 04:47:12 +00:00
if (args.mode === 'list') {
logger.info({ rules: activeRules }, 'listing active rules');
2019-06-25 04:47:12 +00:00
return STATUS_SUCCESS;
}
for (const data of docs) {
await visitRules(ctx, activeRules, data);
2019-06-15 20:20:04 +00:00
}
if (ctx.errors.length > 0) {
logger.error({ count: ctx.errors.length, errors: ctx.errors }, 'some rules failed');
2019-06-17 13:04:58 +00:00
if (args.count) {
return Math.min(ctx.errors.length, 255);
} else {
return STATUS_ERROR;
}
} else {
logger.info('all rules passed');
const output = parser.dump(...docs);
2019-06-17 13:04:58 +00:00
await writeSource(args.dest, output);
return STATUS_SUCCESS;
}
2019-06-15 20:20:04 +00:00
}
main(process.argv).then((status) => process.exit(status)).catch((err) => {
/* tslint:disable-next-line:no-console */
console.error('uncaught error during main:', err);
process.exit(STATUS_ERROR);
});