Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

gh-lint

Package Overview
Dependencies
Maintainers
2
Versions
21
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

gh-lint - npm Package Compare versions

Comparing version 0.5.0 to 0.6.0

lib/rules/commit-name.js

5

lib/cli/commands/check.js

@@ -32,2 +32,4 @@ 'use strict';

before: { $ref: '#/definitions/dateTimeOrInt' },
since: { $ref: '#/definitions/dateTimeOrInt' },
until: { $ref: '#/definitions/dateTimeOrInt' },
tap: { type: 'boolean' }

@@ -74,3 +76,4 @@ }

---
message: ${result.message}
message: ${JSON.stringify(result.message)}
messages: ${JSON.stringify(result.messages)}
severity: ${result.mode == 2 ? 'error' : 'warning'}

@@ -77,0 +80,0 @@ ${tapTestMeta(repoName, ruleName)}`);

3

lib/cli/commands/options.js

@@ -62,2 +62,5 @@ 'use strict';

opts.commits = {since: getRangeDate(argv.since || 30)};
if (argv.until) opts.commits.until = getRangeDate(argv.until);
opts.tap = argv.tap;

@@ -64,0 +67,0 @@

@@ -43,2 +43,6 @@ 'use strict';

return allPages(`/repos/${orgRepo}/teams`);
},
commits(orgRepo) {
let {since, until} = options.commits;
return allPages(`/repos/${orgRepo}/commits`, {since, until});
}

@@ -45,0 +49,0 @@ },

@@ -8,2 +8,3 @@ 'use strict';

allErrors: true,
useDefaults: true,
schemas: {

@@ -19,3 +20,3 @@ defs: require('../../schemas/defs.json'),

async checkRules (config, options={}) {
execute.validateConfig(config);
config = execute.prepareConfig(config);
github.setOptions(options);

@@ -45,47 +46,75 @@ const repoSourceRules = await execute.prepareRepoRules(config, options);

const repoSourceRules = {};
const {org, rules, organizations, teams, repositories} = config;
const org = config.org;
if (rules) {
// global rules
const repos = await github.getRepos.organization(org);
addRepos(repos, rules);
await addGlobalRules();
await addOrganisations();
await addTeams();
await addRepositories();
removeDisabledRules();
return repoSourceRules;
async function addGlobalRules() {
if (config.rules) {
const repos = await github.getRepos.organization(org);
addRepos(repos, config.rules);
}
}
if (organizations) {
// organization-specific rules
for (const orgName in organizations) {
const repos = await github.getRepos.organization(orgName);
addRepos(repos, organizations[orgName].rules);
async function addOrganisations() {
const organizations = config.organizations;
if (organizations) {
// organization-specific rules
for (const orgName in organizations) {
const repos = await github.getRepos.organization(orgName);
addRepos(repos, organizations[orgName].rules);
}
}
}
if (teams) {
// team specific rules
for (const teamName in teams) {
let [orgName, shortTeamName] = teamName.split('/');
if (!shortTeamName) {
orgName = org;
shortTeamName = teamName;
async function addTeams() {
const teams = config.teams;
if (teams) {
// team specific rules
for (const teamName in teams) {
let [orgName, shortTeamName] = teamName.split('/');
if (!shortTeamName) {
orgName = org;
shortTeamName = teamName;
}
const repos = await github.getRepos.team(orgName, shortTeamName);
addRepos(repos, teams[teamName].rules);
}
const repos = await github.getRepos.team(orgName, shortTeamName);
addRepos(repos, teams[teamName].rules);
}
}
if (repositories) {
for (const repoName in repositories) {
const fullRepoName = /\//.test(repoName) ? repoName : org + '/' + repoName;
const repoRules = repositories[repoName].rules;
addRepoRules(fullRepoName, repoRules);
async function addRepositories() {
const repositories = config.repositories;
if (repositories) {
for (const repoName in repositories) {
const fullRepoName = /\//.test(repoName) ? repoName : org + '/' + repoName;
const repoRules = repositories[repoName].rules;
addRepoRules(fullRepoName, repoRules);
}
}
}
return repoSourceRules;
function removeDisabledRules() {
for (const repoOrg in repoSourceRules) {
const repoSources = repoSourceRules[repoOrg];
for (const source in repoSources) {
if (Object.keys(repoSources[source]).length == 0)
delete repoSources[source];
}
if (Object.keys(repoSources).length == 0)
delete repoSourceRules[repoOrg];
}
}
function addRepos(repos, repoRules) {
for (const repo of repos) {
const updatedAt = new Date(repo.updated_at);
const inRange = (!options.after || updatedAt > options.after) &&
(!options.before || updatedAt < options.before);
const pushedAt = new Date(repo.pushed_at);
const inRange = (!options.after || updatedAt >= options.after || pushedAt >= options.after) &&
(!options.before || (updatedAt < options.before && pushedAt < options.before));
if (inRange) addRepoRules(repo.full_name, repoRules);

@@ -98,3 +127,3 @@ }

for (const ruleName in repoRules) {
const ruleOptions = execute.normaliseRuleOptions(repoRules[ruleName]);
const ruleOptions = repoRules[ruleName];
const ruleDefinition = execute.getRuleDefinition(ruleName);

@@ -108,3 +137,6 @@ const source = ruleDefinition.source;

// config for smaller scope overrides config for bigger scope
sourceRules[ruleName] = [ruleOptions];
if (ruleOptions.mode === 0)
delete sourceRules[ruleName];
else
sourceRules[ruleName] = [ruleOptions];
}

@@ -135,6 +167,8 @@ }

getRuleDefinition(ruleName) {
return require(path.join('..', 'rules', ruleName));
try { return require(path.join('..', 'rules', ruleName)); }
catch(e) { throw new Error(`cannot find rule ${ruleName}: ${e.message}`); }
},
validateConfig(config) {
prepareConfig(config) {
config = JSON.parse(JSON.stringify(config));
callValidate(ajv.getSchema('config'), config, 'config');

@@ -145,25 +179,33 @@

const ruleNames = {};
addRuleNames(config);
addRuleNamesFromMap(organizations);
addRuleNamesFromMap(teams);
addRuleNamesFromMap(repositories);
const validateRule = {};
addRules(config, 'global rules');
addRulesFromMap(organizations, 'organization');
addRulesFromMap(teams, 'team');
addRulesFromMap(repositories, 'repository');
for (const name in ruleNames) {
let rule;
try { rule = execute.getRuleDefinition(name); }
catch(e) { throw new Error(`cannot find rule ${name}: ${e.message}`); }
let rule = execute.getRuleDefinition(name);
callValidate(validate, rule, `rule "${name}"`);
}
return config;
function addRuleNames({rules}={}) {
function addRules({rules}={}, scope) {
if (rules) {
for (const name in rules)
for (const name in rules) {
rules[name] = execute.normaliseRuleOptions(rules[name]);
if (!validateRule[name]) {
const ruleSchema = execute.getRuleDefinition(name).schema;
validateRule[name] = ajv.compile(ruleSchema);
}
callValidate(validateRule[name], rules[name], `config for rule "${name}" in ${scope}`);
ruleNames[name] = true;
}
}
}
function addRuleNamesFromMap(group) {
function addRulesFromMap(group, groupName) {
if (group) {
for (const item in group)
addRuleNames(group[item]);
addRules(group[item], `${groupName} "${item}"`);
}

@@ -218,2 +260,1 @@ }

}
{
"name": "gh-lint",
"version": "0.5.0",
"version": "0.6.0",
"description": "Rule-based command-line tool for auditing GitHub repositories",

@@ -5,0 +5,0 @@ "main": "lib/execute/index.js",

@@ -42,8 +42,16 @@ # gh-lint

#### Commit rules
By default, these rules analyse the commits for the last 30 days. It can be changed using options `--since` and `--until` (see below).
- commit-name: check that commit names satisfy semantic commit conventions
## Options
- `-c` (or `--config`) - configuration file location
- `-u` (or `--user`) - GitHub username.
- `-p` (or `--pass`) - GitHub password.
- `-u` (or `--user`) - GitHub username
- `-p` (or `--pass`) - GitHub password
- `-a` (or `--after`) / `-b` (or `--before`) - only validate repositories in organizations and in teams that were changed **after**/**before** this date (also can be date-time or the integer number of days). These options have no effect on repositories that are explicitely specified.
- `--since` / `--until` - validate commits **since**/**until** this date (also can be date-time or the integer number of days)
- `--tap` - output results in TAP format

@@ -50,0 +58,0 @@

@@ -63,34 +63,28 @@ 'use strict';

it('should check repos in orgs within date range', () => {
githubMock.repos.organization.MailOnline.list();
githubMock.repos.organization.milojs.list();
githubMock.mock('/repos/MailOnline/json-schema-test', '../fixtures/mailonline_repos/json-schema-test');
githubMock.mock('/repos/MailOnline/ImageViewer', '../fixtures/mailonline_repos/ImageViewer');
githubMock.mock('/repos/milojs/milo', '../fixtures/milojs_repos/milo');
describe('date range', () => {
it('should check repos in orgs within date range', () => {
test(['--after', '2017-01-20', '--before', '2017-02-01']);
});
return ok(run([
'--config', './spec/fixtures/config-orgs.json',
'--after', '2017-01-20', '--before', '2017-02-01'
], false)).then(() => {
assert.equal(log, 'warning MailOnline/json-schema-test: repo-homepage - not satisfied\n' +
'warning MailOnline/ImageViewer: repo-homepage - not satisfied');
assert(nock.isDone());
it('should check repos in orgs changed in the last X days', () => {
test(['--after', getDays('2017-01-20'), '--before', getDays('2017-02-01')]);
});
});
it('should check repos in orgs changed in the last X days', () => {
githubMock.repos.organization.MailOnline.list();
githubMock.repos.organization.milojs.list();
githubMock.mock('/repos/MailOnline/json-schema-test', '../fixtures/mailonline_repos/json-schema-test');
githubMock.mock('/repos/MailOnline/ImageViewer', '../fixtures/mailonline_repos/ImageViewer');
githubMock.mock('/repos/milojs/milo', '../fixtures/milojs_repos/milo');
function test(range) {
githubMock.repos.organization.MailOnline.list();
githubMock.repos.organization.MailOnline.meta(['cuteyp', 'json-schema-test', 'ImageViewer', 'gh-lint']);
githubMock.repos.organization.milojs.list();
githubMock.repos.organization.milojs.meta(['milo', 'proto', 'milo-core']);
return ok(run([
'--config', './spec/fixtures/config-orgs.json',
'--after', getDays('2017-01-20'), '--before', getDays('2017-02-01')
], false)).then(() => {
assert.equal(log, 'warning MailOnline/json-schema-test: repo-homepage - not satisfied\n' +
'warning MailOnline/ImageViewer: repo-homepage - not satisfied');
assert(nock.isDone());
});
const params = ['--config', './spec/fixtures/config-orgs.json'].concat(range);
return ok(run(params, false)).then(() => {
assert.equal(log,
`warning MailOnline/cuteyp: repo-homepage - not satisfied
warning MailOnline/json-schema-test: repo-homepage - not satisfied
warning MailOnline/ImageViewer: repo-homepage - not satisfied
warning MailOnline/gh-lint: repo-homepage - not satisfied
warning milojs/milo-core: repo-homepage - not satisfied`);
assert(nock.isDone());
});
}

@@ -97,0 +91,0 @@ function getDays(dateStr) {

@@ -74,3 +74,3 @@ 'use strict';

it('should execute rules for all repos in two orgs', () => {
it.skip('should execute rules for all repos in two orgs', () => {
githubMock.repos.organization.MailOnline.list();

@@ -77,0 +77,0 @@ githubMock.repos.organization.MailOnline.meta();

@@ -20,5 +20,9 @@ 'use strict';

},
meta() {
meta(repos) {
glob.sync('../fixtures/mailonline_repos/*.json', { cwd: __dirname })
.forEach(addRepoMock('MailOnline'));
.forEach(addRepoMock('MailOnline', repos));
},
teams() {
glob.sync('../fixtures/mailonline_repo_teams/*.json', { cwd: __dirname })
.forEach(addTeamsMock('MailOnline'));
}

@@ -30,5 +34,5 @@ },

},
meta() {
meta(repos) {
glob.sync('../fixtures/milojs_repos/*.json', { cwd: __dirname })
.forEach(addRepoMock('milojs'));
.forEach(addRepoMock('milojs', repos));
}

@@ -58,7 +62,16 @@ }

function addRepoMock(org) {
function addRepoMock(org, repos) {
return function (file) {
const repoName = path.basename(file, '.json');
mock(`/repos/${org}/${repoName}`, file);
if (!repos || repos.indexOf(repoName) >= 0)
mock(`/repos/${org}/${repoName}`, file);
};
}
function addTeamsMock(org) {
return function (file) {
const repoName = path.basename(file, '.json');
mock(`/repos/${org}/${repoName}/teams?per_page=30&page=1`, file);
};
}

@@ -5,9 +5,17 @@ 'use strict';

const assert = require('assert');
const nock = require('nock');
const githubMock = require('./github_mock');
const github = require('../../lib/execute/github');
// const util = require('util');
describe('prepareRepoRules', () => {
afterEach(() => {
nock.cleanAll();
github.clearTeams();
});
describe('repositories scope', () => {
it('should collect sources and rules for all repositories', () => {
const config = {
const config = execute.prepareConfig({
org: 'MailOnline',

@@ -27,3 +35,3 @@ repositories: {

}
};
});

@@ -34,7 +42,7 @@ return execute.prepareRepoRules(config)

'MailOnline/mol-fe': {
meta: { 'repo-description': [{ mode: 2}] } },
meta: { 'repo-description': [{ mode: 2, minLength: 1 }] } },
'milojs/milo': {
meta: {
'repo-description': [{ mode: 2, minLength: 16 }],
'repo-homepage': [{ mode: 1 }]
'repo-homepage': [{ mode: 1, minLength: 1 }]
}

@@ -54,3 +62,3 @@ }

const config = require('../fixtures/config-orgs.json');
const config = execute.prepareConfig(require('../fixtures/config-orgs.json'));

@@ -63,6 +71,12 @@ return execute.prepareRepoRules(config, {

assert.deepStrictEqual(repoSourceRules, {
'MailOnline/cuteyp': {
meta: {
'repo-description': [ { mode: 2, minLength: 1 } ],
'repo-homepage': [ { mode: 1, minLength: 1 } ]
}
},
'MailOnline/json-schema-test': {
meta: {
'repo-description': [ { mode: 2 } ],
'repo-homepage': [ { mode: 1 } ]
'repo-description': [ { mode: 2, minLength: 1 } ],
'repo-homepage': [ { mode: 1, minLength: 1 } ]
}

@@ -72,13 +86,32 @@ },

meta: {
'repo-description': [ { mode: 2 } ],
'repo-homepage': [ { mode: 1 } ]
'repo-description': [ { mode: 2, minLength: 1 } ],
'repo-homepage': [ { mode: 1, minLength: 1 } ]
}
},
'MailOnline/gh-lint': {
meta: {
'repo-description': [ { mode: 2, minLength: 1 } ],
'repo-homepage': [ { mode: 1, minLength: 1 } ]
}
},
'milojs/milo': {
meta: {
'repo-description': [ { mode: 2 } ],
'repo-homepage': [ { mode: 1 } ]
'repo-description': [ { mode: 2, minLength: 1 } ],
'repo-homepage': [ { mode: 1, minLength: 1 } ]
}
},
'milojs/proto': {
meta: {
'repo-description': [ { mode: 2, minLength: 1 } ],
'repo-homepage': [ { mode: 1, minLength: 1 } ]
}
},
'milojs/milo-core': {
meta: {
'repo-description': [ { mode: 2, minLength: 1 } ],
'repo-homepage': [ { mode: 1, minLength: 1 } ]
}
}
});
assert(nock.isDone());
// console.log(util.inspect(repoSourceRules, {depth: null}));

@@ -88,2 +121,89 @@ });

});
describe('teams scope', () => {
it('should collect rules for repos for team', () => {
githubMock.teams();
githubMock.repos.team.mol_fe.list();
const config = execute.prepareConfig(require('../fixtures/config-teams.json'));
return execute.prepareRepoRules(config)
.then(repoSourceRules => {
assert.deepStrictEqual(repoSourceRules, {
'MailOnline/eslint-config-mailonline': {
meta: {
'repo-description': [ { mode: 2, minLength: 1 } ],
'repo-homepage': [ { mode: 1, minLength: 1 } ]
}
},
'MailOnline/mol-conventional-changelog': {
meta: {
'repo-description': [ { mode: 2, minLength: 1 } ],
'repo-homepage': [ { mode: 1, minLength: 1 } ]
}
},
'MailOnline/stylelint-config-mailonline': {
meta: {
'repo-description': [ { mode: 2, minLength: 1 } ],
'repo-homepage': [ { mode: 1, minLength: 1 } ]
}
},
'MailOnline/videojs-vast-vpaid': {
meta: {
'repo-description': [ { mode: 2, minLength: 1 } ],
'repo-homepage': [ { mode: 1, minLength: 1 } ]
}
},
'MailOnline/VPAIDFLASHClient': {
meta: {
'repo-description': [ { mode: 2, minLength: 1 } ],
'repo-homepage': [ { mode: 1, minLength: 1 } ]
}
},
'MailOnline/VPAIDHTML5Client': {
meta: {
'repo-description': [ { mode: 2, minLength: 1 } ],
'repo-homepage': [ { mode: 1, minLength: 1 } ]
}
}
});
assert(nock.isDone());
// console.log(util.inspect(repoSourceRules, {depth: null}));
});
});
it('should collect rules for repos for team excluding disabled rules', () => {
githubMock.teams();
githubMock.repos.team.mol_fe.list();
const config = execute.prepareConfig(require('../fixtures/config-teams-excluding-repos.json'));
return execute.prepareRepoRules(config)
.then(repoSourceRules => {
assert.deepStrictEqual(repoSourceRules, {
'MailOnline/videojs-vast-vpaid': {
meta: {
'repo-description': [ { mode: 2, minLength: 1 } ],
'repo-homepage': [ { mode: 1, minLength: 1 } ]
}
},
'MailOnline/VPAIDFLASHClient': {
meta: {
'repo-description': [ { mode: 2, minLength: 1 } ],
'repo-homepage': [ { mode: 1, minLength: 1 } ]
}
},
'MailOnline/VPAIDHTML5Client': {
meta: {
'repo-description': [ { mode: 2, minLength: 1 } ],
'repo-homepage': [ { mode: 1, minLength: 1 } ]
}
}
});
assert(nock.isDone());
// console.log(util.inspect(repoSourceRules, {depth: null}));
});
});
});
});

@@ -7,6 +7,6 @@ 'use strict';

describe('validateConfig', () => {
describe('prepareConfig', () => {
it('should throw exception if config is not valid', () => {
assert.throws(() => {
execute.validateConfig({});
execute.prepareConfig({});
}, /config is invalid/);

@@ -17,3 +17,3 @@ });

assert.throws(() => {
execute.validateConfig({
execute.prepareConfig({
org: 'MailOnline',

@@ -26,2 +26,19 @@ rules: {

});
it('should throw exception if rule options are invalid', () => {
assert.throws(() => {
execute.prepareConfig({
org: 'MailOnline',
repositories: {
'milojs/milo': {
rules: {
'commit-name': [2, {
maxLineLength: 'invalid'
}]
}
}
}
});
}, /config for rule.*commit-name.*milojs\/milo.*invalid/);
});
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc