Comparing version 0.7.0 to 0.8.0
'use strict'; | ||
const path = require('path'); | ||
const glob = require('glob'); | ||
const github = require('./github'); | ||
@@ -12,9 +13,17 @@ const Ajv = require('ajv'); | ||
rule: require('../../schemas/rule.json'), | ||
config: require('../../schemas/config.json') | ||
config: require('../../schemas/config.json'), | ||
plugin: require('../../schemas/plugin.json') | ||
} | ||
}); | ||
require('ajv-keywords')(ajv, ['typeof', 'patternRequired']); | ||
const RULES = {}, PLUGINS = {}; | ||
const PLUGIN_PREFIX = 'ghlint-plugin-'; | ||
let CORE_LOADED = false; | ||
const execute = module.exports = { | ||
async checkRules (config, options={}) { | ||
if (!CORE_LOADED) loadCoreRules(); | ||
if (config.plugins) loadPlugins(config); | ||
config = execute.prepareConfig(config); | ||
@@ -160,4 +169,4 @@ github.setOptions(options); | ||
getRuleDefinition(ruleName) { | ||
try { return require(path.join('..', 'rules', ruleName)); } | ||
catch(e) { throw new Error(`cannot find rule ${ruleName}: ${e.message}`); } | ||
if (ruleName in RULES) return RULES[ruleName]; | ||
throw new Error(`rule "${ruleName}" is not defined`); | ||
}, | ||
@@ -169,14 +178,8 @@ | ||
const validate = ajv.getSchema('rule'); | ||
const {organizations, teams, repositories} = config; | ||
const ruleNames = {}; | ||
const validateRule = {}; | ||
addRules(config, 'global rules'); | ||
addRulesFromMap(organizations, 'organization'); | ||
addRulesFromMap(teams, 'team'); | ||
addRulesFromMap(repositories, 'repository'); | ||
for (const name in ruleNames) { | ||
let rule = execute.getRuleDefinition(name); | ||
callValidate(validate, rule, `rule "${name}"`); | ||
} | ||
useRules(config, 'global rules'); | ||
useRulesFromMap(organizations, 'organization'); | ||
useRulesFromMap(teams, 'team'); | ||
useRulesFromMap(repositories, 'repository'); | ||
@@ -186,3 +189,3 @@ return config; | ||
function addRules({rules}={}, scope) { | ||
function useRules({rules}={}, scope) { | ||
if (rules) { | ||
@@ -196,3 +199,2 @@ for (const name in rules) { | ||
callValidate(validateRule[name], rules[name], `config for rule "${name}" in ${scope}`); | ||
ruleNames[name] = true; | ||
} | ||
@@ -202,8 +204,21 @@ } | ||
function addRulesFromMap(group, groupName) { | ||
function useRulesFromMap(group, groupName) { | ||
if (group) { | ||
for (const item in group) | ||
addRules(group[item], `${groupName} "${item}"`); | ||
useRules(group[item], `${groupName} "${item}"`); | ||
} | ||
} | ||
}, | ||
addRules(rules, skipValidation) { | ||
const validate = ajv.getSchema('rule'); | ||
for (const name in rules) { | ||
if (name in RULES) | ||
throw new Error(`rule "${name}" is already defined`); | ||
const rule = rules[name]; | ||
if (name != rule.meta.name) | ||
throw new Error(`rule "${name}" has different name in the definition: "${rule.meta.name}"`); | ||
if (!skipValidation) callValidate(validate, rule, `rule "${name}"`); | ||
RULES[name] = rule; | ||
} | ||
} | ||
@@ -213,2 +228,29 @@ }; | ||
function loadCoreRules() { | ||
CORE_LOADED = true; | ||
const ruleFiles = glob.sync('../rules/*.js', {cwd: __dirname}); | ||
const coreRules = {}; | ||
for (const ruleFile of ruleFiles) { | ||
const rule = require(ruleFile); | ||
const name = path.basename(ruleFile, path.extname(ruleFile)); | ||
coreRules[name] = rule; | ||
} | ||
execute.addRules(coreRules); | ||
} | ||
function loadPlugins(config) { | ||
const validate = ajv.getSchema('plugin'); | ||
for (let name of config.plugins) { | ||
if (name.indexOf(PLUGIN_PREFIX) == -1) | ||
name = PLUGIN_PREFIX + name; | ||
if (name in PLUGINS) continue; | ||
const plugin = require(name); | ||
callValidate(validate, plugin, `plugin "${name}"`); | ||
execute.addRules(plugin.rules, true); | ||
PLUGINS[name] = true; | ||
} | ||
} | ||
function callValidate(validate, data, title) { | ||
@@ -215,0 +257,0 @@ if (!validate(data)) |
{ | ||
"name": "gh-lint", | ||
"version": "0.7.0", | ||
"version": "0.8.0", | ||
"description": "Rule-based command-line tool for auditing GitHub repositories", | ||
@@ -22,2 +22,3 @@ "main": "lib/execute/index.js", | ||
"eslint-plugin-promise": "^3.4.0", | ||
"ghlint-plugin-example": "^0.1.0", | ||
"json-schema-test": "^1.3.0", | ||
@@ -24,0 +25,0 @@ "mocha": "^3.2.0", |
@@ -57,3 +57,2 @@ # gh-lint | ||
## Options | ||
@@ -69,4 +68,13 @@ | ||
## Plugins | ||
Rules can be defined in external modules. | ||
The package name must be prefixed with "ghlint-plugin-". In the configuration file a plugin name can be used with or without this prefix. | ||
A plugin package should export an object with a single property "rules" that has a map of rule definitions. Each rule should be valid according to the [rule schema](https://github.com/MailOnline/gh-lint/blob/master/schemas/rule.json). | ||
## License | ||
[MIT](https://github.com/MailOnline/gh-lint/blob/master/LICENSE) |
@@ -18,3 +18,3 @@ { | ||
], | ||
"propertyNames": { "enum": ["org", "organizations", "teams", "repositories", "extends", "rules"] }, | ||
"propertyNames": { "enum": ["org", "organizations", "teams", "repositories", "extends", "rules", "plugins"] }, | ||
"properties": { | ||
@@ -39,2 +39,12 @@ "org": { | ||
"additionalProperties": { "$ref": "#scope" } | ||
}, | ||
"plugins": { | ||
"description": "An optional array of plugin names", | ||
"type": "array", | ||
"items": { | ||
"anyOf": [ | ||
{ "$ref": "defs.json#pluginName" }, | ||
{ "$ref": "defs.json#pluginShortName" } | ||
] | ||
} | ||
} | ||
@@ -41,0 +51,0 @@ } |
@@ -29,4 +29,16 @@ { | ||
"pattern": "^(?:(?:(?:[A-Za-z0-9]+[-]?)+[A-Za-z0-9]\/)?[A-Za-z0-9_-]+\\s*,\\s*)*(?:(?:[A-Za-z0-9]+[-]?)+[A-Za-z0-9]\/)?[A-Za-z0-9_-]+$" | ||
}, | ||
"pluginName": { | ||
"id": "#pluginName", | ||
"type": "string", | ||
"pattern": "^ghlint-plugin-(?:[A-Za-z0-9]+[-]?)+[A-Za-z0-9]$", | ||
"not": { "pattern": ".{14,}(?:ghlint|plugin)" } | ||
}, | ||
"pluginShortName": { | ||
"id": "#pluginShortName", | ||
"type": "string", | ||
"pattern": "^(?:[A-Za-z0-9]+[-]?)+[A-Za-z0-9]$", | ||
"not": { "pattern": "ghlint|plugin" } | ||
} | ||
} | ||
} |
@@ -22,3 +22,3 @@ 'use strict'; | ||
}); | ||
}, /cannot find rule/); | ||
}, /is not defined/); | ||
}); | ||
@@ -25,0 +25,0 @@ |
@@ -16,2 +16,3 @@ 'use strict'; | ||
ajv.addSchema(require('../schemas/config.json')); | ||
ajv.addSchema(require('../schemas/plugin.json')); | ||
@@ -18,0 +19,0 @@ |
@@ -77,2 +77,42 @@ [ | ||
"valid": false | ||
}, | ||
{ | ||
"description": "a valid configuration with plugins", | ||
"data": { | ||
"org": "MailOnline", | ||
"rules": { | ||
"rule-1": 2 | ||
}, | ||
"plugins": [ | ||
"mol-repo-names", | ||
"ghlint-plugin-mol-pr-names" | ||
] | ||
}, | ||
"valid": true | ||
}, | ||
{ | ||
"description": "an invalid configuration with plugins (invalid plugin name)", | ||
"data": { | ||
"org": "MailOnline", | ||
"rules": { | ||
"rule-1": 2 | ||
}, | ||
"plugins": [ | ||
"mol-plugin" | ||
] | ||
}, | ||
"valid": false | ||
}, | ||
{ | ||
"description": "an invalid configuration with plugins (plugins config is not an array)", | ||
"data": { | ||
"org": "MailOnline", | ||
"rules": { | ||
"rule-1": 2 | ||
}, | ||
"plugins": { | ||
"mol-repo-names": true | ||
} | ||
}, | ||
"valid": false | ||
} | ||
@@ -79,0 +119,0 @@ ] |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
880996
168
16633
79
9
6