Skip to content

Commit

Permalink
feat: Add check command that compares GitHub contributors with credit…
Browse files Browse the repository at this point in the history
…ed ones (#58)

* feat: Add checking functionnality that compares contributors with GH data

* fix(check): Use the info from config file, paginate GH data

* doc(check): Document the new check command

* fix(eslint): Add missing semicolon

* tests: Add tests for utils.check

* fix: Check for code and test only

* refactor: use includes and correct awaits

* refactor: more includes and template literals
  • Loading branch information
machour authored and Kent C. Dodds committed Nov 8, 2017
1 parent b443c82 commit 88c7a29
Show file tree
Hide file tree
Showing 14 changed files with 8,405 additions and 3 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,17 @@ Where `username` is the user's GitHub username, and `contribution` is a `,`-sepa
- tutorial: [](# "Tutorials")
- video: [📹](# "Videos")

### Check for missing contributors

Use `check` to compare contributors from GitHub with the ones credited in your `.all-contributorsrc` file, in order to make sure that credit is given where it's due.

```console
all-contributors check
```

> Due to GitHub API restrictions, this command only works for projects with less than 500 contributors.

## Configuration

You can configure the project by updating the `.all-contributorsrc` JSON file. The data used to generate the contributors list will be stored in there, and you can configure how you want `all-contributors-cli` to generate the list.
Expand Down
39 changes: 39 additions & 0 deletions cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

var path = require('path');
var yargs = require('yargs');
var chalk = require('chalk');
var inquirer = require('inquirer');

var init = require('./lib/init');
Expand All @@ -23,6 +24,8 @@ var argv = yargs
.usage('Usage: $0 add <username> <contribution>')
.command('init', 'Prepare the project to be used with this tool')
.usage('Usage: $0 init')
.command('check', 'Compares contributors from GitHub with the ones credited in .all-contributorsrc')
.usage('Usage: $0 check')
.boolean('commit')
.default('files', ['README.md'])
.default('contributorsPerLine', 7)
Expand Down Expand Up @@ -68,6 +71,37 @@ function addContribution(argv) {
});
}

function checkContributors() {
var configData = util.configFile.readConfig(argv.config);

return util.check(configData.projectOwner, configData.projectName)
.then(ghContributors => {
var knownContributions = configData.contributors.reduce((obj, item) => {
obj[item.login] = item.contributions;
return obj;
}, {});
var knownContributors = configData.contributors.map(contributor => contributor.login);

var missingInConfig = ghContributors.filter(login => !knownContributors.includes(login));
var missingFromGithub = knownContributors.filter(login => {
return !ghContributors.includes(login) && (
knownContributions[login].includes('code') ||
knownContributions[login].includes('test')
);
});

if (missingInConfig.length) {
process.stdout.write(chalk.bold('Missing contributors in .all-contributorsrc:\n'));
process.stdout.write(` ${missingInConfig.join(', ')}\n`);
}

if (missingFromGithub.length) {
process.stdout.write(chalk.bold('Unknown contributors found in .all-contributorsrc:\n'));
process.stdout.write(` ${missingFromGithub.join(', ')}\n`);
}
});
}

function onError(error) {
if (error) {
console.error(error.message);
Expand All @@ -87,6 +121,9 @@ function promptForCommand(argv) {
}, {
name: 'Re-generate the contributors list',
value: 'generate'
}, {
name: 'Compare contributors from GitHub with the credited ones',
value: 'check'
}],
when: !argv._[0],
default: 0
Expand All @@ -107,6 +144,8 @@ promptForCommand(argv)
return startGeneration(argv);
case 'add':
return addContribution(argv);
case 'check':
return checkContributors();
default:
throw new Error(`Unknown command ${command}`);
}
Expand Down
45 changes: 45 additions & 0 deletions lib/util/check.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
'use strict';

var pify = require('pify');
var request = pify(require('request'));

function getNextLink(link) {
if (!link) {
return null;
}

var nextLink = link.split(',').find(s => s.includes('rel="next"'));

if (!nextLink) {
return null;
}

return nextLink.split(';')[0].slice(1, -1);
}

function getContributorsPage(url) {
return request.get({
url: url,
headers: {
'User-Agent': 'request'
}
})
.then(res => {
var body = JSON.parse(res.body);
var contributorsIds = body.map(contributor => contributor.login);

var nextLink = getNextLink(res.headers.link);
if (nextLink) {
return getContributorsPage(nextLink).then(nextContributors => {
return contributorsIds.concat(nextContributors);
});
}

return contributorsIds;
});
}

module.exports = function getContributorsFromGithub(owner, name) {
var url = `https://api.github.com/repos/${owner}/${name}/contributors?per_page=100`;
return getContributorsPage(url);
};
46 changes: 46 additions & 0 deletions lib/util/check.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@

import test from 'ava';
import nock from 'nock';

var check = require('./check');
import allContributorsCliResponse from './fixtures/all-contributors.response.json';
import allContributorsCliTransformed from './fixtures/all-contributors.transformed.json';

import reactNativeResponse1 from './fixtures/react-native.response.1.json';
import reactNativeResponse2 from './fixtures/react-native.response.2.json';
import reactNativeResponse3 from './fixtures/react-native.response.3.json';
import reactNativeResponse4 from './fixtures/react-native.response.4.json';
import reactNativeTransformed from './fixtures/react-native.transformed.json';

test.before(() => {
nock('https://api.github.com')
.persist()
.get('/repos/jfmengels/all-contributors-cli/contributors?per_page=100')
.reply(200, allContributorsCliResponse)
.get('/repos/facebook/react-native/contributors?per_page=100')
.reply(200, reactNativeResponse1, {
Link: '<https://api.github.com/repositories/29028775/contributors?per_page=100&page=2>; rel="next", <https://api.github.com/repositories/29028775/contributors?per_page=100&page=4>; rel="last"'
})
.get('/repositories/29028775/contributors?per_page=100&page=2')
.reply(200, reactNativeResponse2, {
Link: '<https://api.github.com/repositories/29028775/contributors?per_page=100&page=3>; rel="next", <https://api.github.com/repositories/29028775/contributors?per_page=100&page=4>; rel="last", <https://api.github.com/repositories/29028775/contributors?per_page=100&page=1>; rel="first", <https://api.github.com/repositories/29028775/contributors?per_page=100&page=1>; rel="prev"'
})
.get('/repositories/29028775/contributors?per_page=100&page=3')
.reply(200, reactNativeResponse3, {
Link: '<https://api.github.com/repositories/29028775/contributors?per_page=100&page=4>; rel="next", <https://api.github.com/repositories/29028775/contributors?per_page=100&page=4>; rel="last", <https://api.github.com/repositories/29028775/contributors?per_page=100&page=1>; rel="first", <https://api.github.com/repositories/29028775/contributors?per_page=100&page=2>; rel="prev"'
})
.get('/repositories/29028775/contributors?per_page=100&page=4')
.reply(200, reactNativeResponse4, {
Link: '<https://api.github.com/repositories/29028775/contributors?per_page=100&page=1>; rel="first", <https://api.github.com/repositories/29028775/contributors?per_page=100&page=3>; rel="prev"'
});
});

test('Handle a single results page correctly', async t => {
const transformed = await check('jfmengels', 'all-contributors-cli');
t.deepEqual(transformed, allContributorsCliTransformed);
});

test('Handle multiple results pages correctly', async t => {
const transformed = await check('facebook', 'react-native');
t.deepEqual(transformed, reactNativeTransformed);
});
Loading

0 comments on commit 88c7a29

Please sign in to comment.