From 9324e5cdb32d0cd3882172de9b7700b97e7f8cb0 Mon Sep 17 00:00:00 2001 From: Thomas Getgood Date: Mon, 27 Nov 2017 16:27:47 -0500 Subject: [PATCH] feat: added config file support. --- .gitignore | 1 + config.json.example | 16 +++++++++++ readme.md | 51 ++++++++++++++++++++++++++++++++++ src/cli.js | 18 ++++++++++-- src/index.js | 67 ++++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 147 insertions(+), 6 deletions(-) create mode 100644 config.json.example diff --git a/.gitignore b/.gitignore index 15de390..dd5f847 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules +config.json data diff --git a/config.json.example b/config.json.example new file mode 100644 index 0000000..b0f4d0b --- /dev/null +++ b/config.json.example @@ -0,0 +1,16 @@ +{ + "token": "123435abcdf", + "repos": [{ + "login": "mntnr", + "repo": "name-your-contributors", + "before": "2017-11-30", + "after": "2017-06-01" + }, { + "login": "mntnr", + "repo": "whodidwhat" + }], + "orgs": [{ + "login": "adventure-js", + "after": "2017-07-01" + }] +} diff --git a/readme.md b/readme.md index a2b45ad..ecec07b 100644 --- a/readme.md +++ b/readme.md @@ -79,6 +79,57 @@ $ export GITHUB_TOKEN={your-token} $ name-your-contributors -u mntnr -r name-your-contributors $ name-your-contributors -o ipfs -a 2017-01-01 > ipfs-contrib.json + +$ name-your-contributors --config config.json > combined-out.json +``` + +### Config File + +For batching convenience, Name Your Contributors takes a config file which +specifies a token, a list of repos, and a list of orgs to grab. The +`config.json.example` is an outline of this file format: + +```json +{ + "token": "123435abcdf", + "repos": [{ + "login": "mntnr", + "repo": "name-your-contributors", + "before": "2017-11-30", + "after": "2017-06-01" + }, { + "login": "mntnr", + "repo": "whodidwhat" + }], + "orgs": [{ + "login": "adventure-js", + "after": "2017-07-01" + }] +} +``` + +A token passed in the config file will override any token present in the +environment. + +The output when passed a config file is a mirror of the config file with the +token removed and a `contributions` key added to each object, like so: + +```json +{ + "repos": [{ + "login": "mntnr", + "repo": "name-your-contributors", + "before": "2017-11-30", + "after": "2017-06-01", + "contributions" : { + "commitAuthors": [...], + "commitCommentators: [...], + ,,, + }, + ... + }], + "orgs": [...] +} ``` The output will be in the format: diff --git a/src/cli.js b/src/cli.js index be373c1..4733daf 100755 --- a/src/cli.js +++ b/src/cli.js @@ -16,6 +16,8 @@ const cli = meow([` -r, --repo - Repository to search -t, --token - GitHub auth token to use -u, --user - User to which repository belongs + --config - Operate from config file. In this mode only token, verbose, and + debug flags apply. Authentication This script looks for an auth token in the env var GITHUB_TOKEN. Make sure @@ -25,6 +27,8 @@ const cli = meow([` $ name-your-contributors -r ipfs -u ipfs --after=2016-01-15T00:20:24Z --before=2016-01-20T00:20:24Z $ name-your-contributors -o ipfs -a 2017-01-01 > ipfs-contrib-2017.json + + $ name-your-contributors --config config.json > combined-out.json `], { alias: { a: 'after', @@ -43,7 +47,7 @@ const token = cli.flags.t || process.env.GITHUB_TOKEN const after = cli.flags.a ? new Date(cli.flags.a) : new Date(0) const before = cli.flags.b ? new Date(cli.flags.b) : new Date() -if (!token) { +if (!token && !cli.flags.config) { console.error('A token is needed to access the GitHub API. Please provide one with -t or the GITHUB_TOKEN environment variable.') process.exit(1) } @@ -76,7 +80,17 @@ const callWithDefaults = (f, opts) => { const fetchRepo = (user, repo) => callWithDefaults(main.repoContributors, {user, repo}) -if (cli.flags.o) { +if (cli.flags.config) { + main.fromConfig({ + file: cli.flags.config, + token, + verbose: cli.flags.v, + debug: cli.flags.debug, + dryRun: cli.flags.dryRun + }).then(x => JSON.stringify(x, null, 2)) + .then(handleOut) + .catch(handleError) +} else if (cli.flags.o) { callWithDefaults(main.orgContributors, {orgName: cli.flags.o}) } else if (cli.flags.u && cli.flags.r) { fetchRepo(cli.flags.u, cli.flags.r) diff --git a/src/index.js b/src/index.js index 95dd006..3952117 100644 --- a/src/index.js +++ b/src/index.js @@ -4,6 +4,7 @@ const graphql = require('./graphql') const queries = require('./queries') const csv = require('csv-writer').createArrayCsvStringifier const exec = require('child_process').exec +const fs = require('fs') // // Shell Helpers @@ -65,6 +66,9 @@ const verifyResultHasKey = (key, query) => } } +// +// Config File Parsing + // // API // @@ -76,14 +80,14 @@ const verifyResultHasKey = (key, query) => * @param before - only return contributions before this timestamp * @param after - only return contributions after this timestamp */ -const repoContributors = ( - {token, user, repo, before, after, debug, dryRun, verbose}) => +const repoContributors = + ({token, user, repo, before, after, debug, dryRun, verbose}) => graphql.executequery({ token, debug, dryRun, verbose, - name: 'repoContributors', + name: `repoContributors: ${user}/${repo}`, query: queries.repository(repo, user, before, after) }).then(verifyResultHasKey('repository', user + '/' + repo)) .then(json => { @@ -106,7 +110,7 @@ const orgContributors = ({token, orgName, before, after, debug, dryRun, verbose} debug, dryRun, verbose, - name: 'orgContributors', + name: `orgContributors: ${orgName}`, query: queries.orgRepos(orgName, before, after) }).then(verifyResultHasKey('organization', orgName)) .then(data => { @@ -117,6 +121,60 @@ const orgContributors = ({token, orgName, before, after, debug, dryRun, verbose} } }) +/** Returns all contributions to repos and orgs specified in `file` + * @param token - GitHub auth token + * @param file - Config file path + */ +const fromConfig = async ({token, file, verbose, debug, dryRun}) => { + const config = JSON.parse(fs.readFileSync(file)) + const ght = config.token || token + if (!ght) { + throw new Error('No token specified in config or arguments. Aborting.') + } + const repoResults = config.repos.map(({login, repo, before, after}) => { + const afterDate = after ? new Date(after) : new Date(0) + const beforeDate = before ? new Date(before) : new Date() + + return repoContributors({ + token: ght, + user: login, + repo, + before: beforeDate, + after: afterDate, + debug, + dryRun, + verbose + }) + }) + + const orgResults = config.orgs.map(({login, before, after}) => { + const afterDate = after ? new Date(after) : new Date(0) + const beforeDate = before ? new Date(before) : new Date() + return orgContributors({ + orgName: login, + before: beforeDate, + after: afterDate, + token: ght, + verbose, + debug, + dryRun + }) + }) + + return { + repos: (await Promise.all(repoResults)).map((result, index) => { + const repo = config.repos[index] + repo.contributions = result + return repo + }), + orgs: (await Promise.all(orgResults)).map((result, index) => { + const org = config.orgs[index] + org.contributions = result + return org + }) + } +} + /** Returns the login of the user to whom the given token is registered. * @param token - GitHub Auth token */ @@ -128,6 +186,7 @@ const currentUser = token => module.exports = { toCSV, + fromConfig, parseGitURL, getCurrentRepoInfo, currentUser,