From 469fdbfcaeabf985974f2d4c1fb0ae6b22daf2d3 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 21 Apr 2020 23:04:46 +0200 Subject: [PATCH 1/3] fix: Adjusting file access that was clashing with uncontrolled promises --- package.json | 9 +- src/cli.js | 194 +++++++++++++++++--------------------- src/discover/index.js | 7 +- src/discover/learner.js | 37 ++++---- src/discover/learner.json | 0 5 files changed, 116 insertions(+), 131 deletions(-) delete mode 100644 src/discover/learner.json diff --git a/package.json b/package.json index 0f3caca8..32f21dcc 100644 --- a/package.json +++ b/package.json @@ -42,16 +42,17 @@ }, "homepage": "https://github.com/all-contributors/all-contributors-cli#readme", "dependencies": { - "@babel/runtime": "^7.2.0", - "ac-learn": "^1.0.3", + "@babel/runtime": "^7.7.6", + "ac-learn": "^1.5.1", "async": "^3.0.1", "chalk": "^2.3.0", "clui": "^0.3.6", "didyoumean": "^1.2.1", + "name-your-contributors": "^3.8.3", "inquirer": "^6.2.1", - "json-fixer": "^1.3.1-0", + "json-fixer": "^1.4.1", + "inquirer": "^7.0.4", "lodash": "^4.11.2", - "name-your-contributors": "^3.4.0", "pify": "^4.0.1", "request": "^2.72.0", "yargs": "^13.1.0" diff --git a/src/cli.js b/src/cli.js index 0a7f5cbb..2ce412fb 100755 --- a/src/cli.js +++ b/src/cli.js @@ -16,7 +16,7 @@ const util = require('./util') const repo = require('./repo') const updateContributors = require('./contributors') const {getContributors} = require('./discover') -const learner = require('./discover/learner') +const {getLearner} = require('./discover/learner') const cwd = process.cwd() const defaultRCFile = path.join(cwd, '.all-contributorsrc') @@ -130,7 +130,6 @@ function checkContributors(argv) { configData.repoHost, ) .then(repoContributors => { - // console.dir(repoContributors) //['jfmengels', 'jakebolam', ...] const checkKey = repo.getCheckKey(configData.repoType) const knownContributions = configData.contributors.reduce((obj, item) => { obj[item[checkKey]] = item.contributions @@ -167,120 +166,97 @@ function checkContributors(argv) { }) } -function fetchContributors(argv) { - // console.log('argv=', argv); - // const configData = util.configFile.readConfig(argv.config) - // console.log('configData') - // console.dir(configData) - - return getContributors(argv.projectOwner, argv.projectName).then( - repoContributors => { - // repoContributors = {prCreators, prCommentators, issueCreators, issueCommentators, reviewers, commitAuthors, commitCommentators} - // console.dir(repoContributors) - - // const checkKey = repo.getCheckKey(configData.repoType) - // const knownContributions = configData.contributors.reduce((obj, item) => { - // obj[item[checkKey]] = item.contributions - // return obj - // }, {}) - // console.log('knownContributions', knownContributions) //{ jfmengels: ['code', 'test', 'doc'], ...} - // const knownContributors = configData.contributors.map( - // contributor => contributor[checkKey], - // ) - // console.log('knownContributors', knownContributors) //['kentcdodds', 'ben-eb', ...] - - // let contributors = new Set( - // repoContributors.prCreators.map(usr => usr.login), - // ) - - // repoContributors.issueCreators.forEach(usr => contributors.add(usr.login)) - // repoContributors.reviewers.forEach(usr => contributors.add(usr.login)) - // repoContributors.commitAuthors.forEach(usr => contributors.add(usr.login)) - // contributors = Array.from(contributors) - - // console.log('ctbs=', contributors); - - //~1. Auto-add reviewers for review~ - //~2. Auto-add issue creators for any categories found~ - //~3. Auto-add commit authors~ - //4. Roll onto other contribution categories following https://www.draw.io/#G1uL9saIuZl3rj8sOo9xsLOPByAe28qhwa - - const args = {...argv, _: []} - const contributorsToAdd = [] - repoContributors.reviewers.forEach(usr => { - // args._ = ['add', usr.login, 'review'] - // addContribution(args) - contributorsToAdd.push({login: usr.login, contributions: ['review']}) - // console.log( - // `Adding ${chalk.underline('Reviewer')} ${chalk.blue(usr.login)}`, - // ) - }) +async function fetchContributors(argv) { + const {reviewers, commitAuthors, issueCreators} = await getContributors( + argv.projectOwner, + argv.projectName, + ) + const args = {...argv, _: []} + const contributorsToAdd = [] + const learner = await getLearner() - repoContributors.issueCreators.forEach(usr => { - // console.log('usr=', usr.login, 'labels=', usr.labels) - const contributor = { - login: usr.login, - contributions: [], - } - usr.labels.forEach(lbl => { - const guesses = learner.classify(lbl).filter(c => c && c !== 'null') - if (guesses.length) { - const category = guesses[0] - // args._ = ['', usr.login, category] - // addContribution(args) - if (!contributor.contributions.includes(category)) - contributor.contributions.push(category) - // console.log( - // `Adding ${chalk.blue(usr.login)} for ${chalk.underline(category)}`, - // ) - } //else console.warn(`Oops, I couldn't find any category for the "${lbl}" label`) - }) - const existingContributor = contributorsToAdd.filter( - ctrb => ctrb.login === usr.login, - ) - if (existingContributor.length) { - existingContributor[0].contributions = [ - ...new Set( - existingContributor[0].contributions.concat( - contributor.contributions, - ), - ), - ] - } else contributorsToAdd.push(contributor) - }) + reviewers.forEach(usr => { + contributorsToAdd.push({login: usr.login, contributions: ['review']}) + + console.log( + `Adding ${chalk.underline('Reviewer')} ${chalk.blue(usr.login)}`, + ) + }) + + issueCreators.forEach(usr => { + const contributor = { + login: usr.login, + contributions: [], + } + + usr.labels.forEach(lbl => { + const guessedCategory = learner.classify(lbl).find(c => c && c !== 'null') - repoContributors.commitAuthors.forEach(usr => { - // const contributor = { - // login: usr.login, - // contributions: [], - // } - // console.log('commit auth:', usr) - const existingContributor = contributorsToAdd.filter( - ctrb => ctrb.login === usr.login, + if (!guessedCategory) { + console.warn( + `Oops, I couldn't find any category for the "${lbl}" label`, ) - if (existingContributor.length) { - //there's no label or commit message info so use only code for now - if (!existingContributor[0].contributions.includes('code')) { - existingContributor[0].contributions.push('code') - } - } else - contributorsToAdd.push({login: usr.login, contributions: ['code']}) - }) - // console.log('contributorsToAdd=', contributorsToAdd) - contributorsToAdd.forEach(contributor => { + return + } + + if (!contributor.contributions.includes(guessedCategory)) { + contributor.contributions.push(guessedCategory) + console.log( - `Adding ${chalk.blue(contributor.login)} for ${chalk.underline( - contributor.contributions.join('/'), + `Adding ${chalk.blue(usr.login)} for ${chalk.underline( + guessedCategory, )}`, ) - args._ = ['', contributor.login, contributor.contributions.join(',')] - // if (contributor.contributions.length) addContribution(args) - // else console.log('Skipping', contributor.login) - }) - }, - err => console.error('fetch error:', err), - ) + } + }) + + const existingContributor = contributorsToAdd.find( + c => c.login === usr.login, + ) + + if (existingContributor) { + existingContributor.contributions.push(...contributor.contributions) + } else { + contributorsToAdd.push(contributor) + } + }) + + commitAuthors.forEach(usr => { + const existingContributor = contributorsToAdd.find( + c => c.login === usr.login, + ) + + if (existingContributor) { + // There's no label or commit message info so use only code for now + if (!existingContributor.contributions.includes('code')) { + existingContributor.contributions.push('code') + } + } else { + contributorsToAdd.push({login: usr.login, contributions: ['code']}) + } + }) + + // TODO: Roll onto other contribution categories following https://www.draw.io/#G1uL9saIuZl3rj8sOo9xsLOPByAe28qhwa + + for (const contributor of contributorsToAdd) { + if (!contributor.contributions.length) { + console.log('Skipping', contributor.login) + + return + } + + console.log( + `Adding ${chalk.blue(contributor.login)} for ${chalk.underline( + contributor.contributions.join('/'), + )}`, + ) + + args._ = ['', contributor.login, contributor.contributions.join(',')] + + /* eslint-disable no-await-in-loop */ + await addContribution(args) + } } function onError(error) { diff --git a/src/discover/index.js b/src/discover/index.js index 6c4ee396..4b69ea73 100644 --- a/src/discover/index.js +++ b/src/discover/index.js @@ -4,15 +4,18 @@ const {Spinner} = require('clui') const privateToken = (process.env && process.env.PRIVATE_TOKEN) || '' const loader = new Spinner('Loading...') -const getContributors = function(owner, name, token = privateToken) { +const getContributors = async function(owner, name, token = privateToken) { loader.start() - const contributors = nyc.repoContributors({ + + const contributors = await nyc.repoContributors({ token, user: owner, repo: name, commits: true, }) + loader.stop() + return contributors } diff --git a/src/discover/learner.js b/src/discover/learner.js index 5afe2121..3a19d859 100644 --- a/src/discover/learner.js +++ b/src/discover/learner.js @@ -3,21 +3,26 @@ const Learner = require('ac-learn') const JSON_PATH = `${__dirname}/learner.json` -//@TODO: Use the JSON methods from `ac-learn` to get the whole thing saveable -const learner = new Learner() -/* eslint-disable no-console */ -if (existsSync(JSON_PATH)) { - learner.loadAndDeserializeClassifier(JSON_PATH).then(classifier => { - learner.classifier = classifier - // console.log('Re-using existing classifier') - }, console.error) -} else { - learner.crossValidate(6) - learner.eval() - learner.serializeAndSaveClassifier(JSON_PATH).then(_ => { - // console.log('Classifier saved', classifier) - }, console.error) +async function getLearner() { + const learner = new Learner() + + try { + if (existsSync(JSON_PATH)) { + learner.classifier = await learner.loadAndDeserializeClassifier(JSON_PATH) + } else { + learner.crossValidate(6) + learner.eval() + + await learner.serializeAndSaveClassifier(JSON_PATH) + } + } catch (e) { + /* eslint-disable no-console */ + console.error(e) + } + + return learner } -/* eslint-enable no-console */ -module.exports = learner +module.exports = { + getLearner, +} diff --git a/src/discover/learner.json b/src/discover/learner.json deleted file mode 100644 index e69de29b..00000000 From 36f0398f8f46f4be26f822aea188a33c740d51d1 Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 24 May 2020 02:05:27 +0200 Subject: [PATCH 2/3] review: Adding code review changing and linting --- .eslintrc.js | 9 +-- package.json | 1 - src/cli.js | 186 ++++++++++++++++++++++++++++----------------------- 3 files changed, 106 insertions(+), 90 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index bf679f97..b2dc3918 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,13 +1,14 @@ module.exports = { extends: [ - require.resolve('eslint-config-kentcdodds'), - require.resolve('eslint-config-kentcdodds/jest'), - ], + require.resolve('eslint-config-kentcdodds'), + require.resolve('eslint-config-kentcdodds/jest'), + ], rules: { 'func-names': 0, 'babel/camelcase': 0, 'import/extensions': 0, 'consistent-return': 0, 'no-process-exit': 0, - } + 'no-continue': 0, + }, } diff --git a/package.json b/package.json index 32f21dcc..2e80ece8 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,6 @@ "clui": "^0.3.6", "didyoumean": "^1.2.1", "name-your-contributors": "^3.8.3", - "inquirer": "^6.2.1", "json-fixer": "^1.4.1", "inquirer": "^7.0.4", "lodash": "^4.11.2", diff --git a/src/cli.js b/src/cli.js index 2ce412fb..1583514e 100755 --- a/src/cli.js +++ b/src/cli.js @@ -52,19 +52,27 @@ const yargv = yargs } }).argv +function onError(error) { + if (error) { + console.error(error.message) + process.exit(1) + } + + process.exit(0) +} + function suggestCommands(cmd) { const availableCommands = ['generate', 'add', 'init', 'check'] const suggestion = didYouMean(cmd, availableCommands) - if (suggestion) { - console.log(chalk.bold(`Did you mean ${suggestion}`)) - } + if (suggestion) console.log(chalk.bold(`Did you mean ${suggestion}`)) } function startGeneration(argv) { return Promise.all( argv.files.map(file => { const filePath = path.join(cwd, file) + return util.markdown.read(filePath).then(fileContent => { const newFileContent = generate(argv, argv.contributors, fileContent) return util.markdown.write(filePath, newFileContent) @@ -73,7 +81,7 @@ function startGeneration(argv) { ) } -function addContribution(argv) { +async function addContribution(argv) { /* Example: (for clarity & debugging purposes) { _: [ 'add' ], @@ -94,76 +102,79 @@ function addContribution(argv) { */ const username = argv._[1] const contributions = argv._[2] + // Add or update contributor in the config file - return updateContributors(argv, username, contributions).then( - data => { - argv.contributors = data.contributors - /* Example - [ { login: 'Berkmann18', - name: 'Maximilian Berkmann', - avatar_url: 'https://avatars0.githubusercontent.com/u/8260834?v=4', - profile: 'http://maxcubing.wordpress.com', - contributions: [ 'code', 'ideas' ] }, - { already in argv.contributors } ] - */ - return startGeneration(argv).then( - () => { - if (argv.commit) { - return util.git.commit(argv, data) - } - }, - err => console.error('Generation fail:', err), - ) - }, - err => console.error('Contributor Update fail:', err), - ) + let data + + try { + data = updateContributors(argv, username, contributions) + } catch (error) { + return console.error('Contributor Update fail:', error) + } + + argv.contributors = data.contributors + + /* Example + [ { login: 'Berkmann18', + name: 'Maximilian Berkmann', + avatar_url: 'https://avatars0.githubusercontent.com/u/8260834?v=4', + profile: 'http://maxcubing.wordpress.com', + contributions: [ 'code', 'ideas' ] }, + { already in argv.contributors } ] + */ + + try { + await startGeneration(argv) + + return argv.commit ? util.git.commit(argv, data) : null + } catch (error) { + console.error('Generation fail:', error) + } } -function checkContributors(argv) { +async function checkContributors(argv) { const configData = util.configFile.readConfig(argv.config) - return repo - .getContributors( - configData.projectOwner, - configData.projectName, - configData.repoType, - configData.repoHost, - ) - .then(repoContributors => { - const checkKey = repo.getCheckKey(configData.repoType) - const knownContributions = configData.contributors.reduce((obj, item) => { - obj[item[checkKey]] = item.contributions - return obj - }, {}) - const knownContributors = configData.contributors.map( - contributor => contributor[checkKey], - ) + const repoContributors = await repo.getContributors( + configData.projectOwner, + configData.projectName, + configData.repoType, + configData.repoHost, + ) - const missingInConfig = repoContributors.filter( - key => !knownContributors.includes(key), - ) - const missingFromRepo = knownContributors.filter(key => { - return ( - !repoContributors.includes(key) && - (knownContributions[key].includes('code') || - knownContributions[key].includes('test')) - ) - }) + const checkKey = repo.getCheckKey(configData.repoType) + const knownContributions = configData.contributors.reduce((obj, item) => { + obj[item[checkKey]] = item.contributions + return obj + }, {}) + const knownContributors = configData.contributors.map( + contributor => contributor[checkKey], + ) - if (missingInConfig.length) { - process.stdout.write( - chalk.bold('Missing contributors in .all-contributorsrc:\n'), - ) - process.stdout.write(` ${missingInConfig.join(', ')}\n`) - } + const missingInConfig = repoContributors.filter( + key => !knownContributors.includes(key), + ) + const missingFromRepo = knownContributors.filter(key => { + return ( + !repoContributors.includes(key) && + (knownContributions[key].includes('code') || + knownContributions[key].includes('test')) + ) + }) - if (missingFromRepo.length) { - process.stdout.write( - chalk.bold('Unknown contributors found in .all-contributorsrc:\n'), - ) - process.stdout.write(`${missingFromRepo.join(', ')}\n`) - } - }) + if (missingInConfig.length) { + process.stdout.write( + chalk.bold('Missing contributors in .all-contributorsrc:\n'), + ) + process.stdout.write(` ${missingInConfig.join(', ')}\n`) + } + + if (missingFromRepo.length) { + process.stdout.write( + chalk.bold('Unknown contributors found in .all-contributorsrc:\n'), + ) + process.stdout.write(`${missingFromRepo.join(', ')}\n`) + } } async function fetchContributors(argv) { @@ -190,7 +201,9 @@ async function fetchContributors(argv) { } usr.labels.forEach(lbl => { - const guessedCategory = learner.classify(lbl).find(c => c && c !== 'null') + const guessedCategory = learner + .classify(lbl) + .find(ctr => ctr && ctr !== 'null') if (!guessedCategory) { console.warn( @@ -212,7 +225,7 @@ async function fetchContributors(argv) { }) const existingContributor = contributorsToAdd.find( - c => c.login === usr.login, + ctr => ctr.login === usr.login, ) if (existingContributor) { @@ -224,7 +237,7 @@ async function fetchContributors(argv) { commitAuthors.forEach(usr => { const existingContributor = contributorsToAdd.find( - c => c.login === usr.login, + ctr => ctr.login === usr.login, ) if (existingContributor) { @@ -243,31 +256,34 @@ async function fetchContributors(argv) { if (!contributor.contributions.length) { console.log('Skipping', contributor.login) - return + continue } + // Format contributor contributions + const contributions = contributor.contributions.join('/') + console.log( `Adding ${chalk.blue(contributor.login)} for ${chalk.underline( - contributor.contributions.join('/'), + contributions, )}`, ) args._ = ['', contributor.login, contributor.contributions.join(',')] - /* eslint-disable no-await-in-loop */ - await addContribution(args) - } -} - -function onError(error) { - if (error) { - console.error(error.message) - process.exit(1) + try { + /* eslint-disable no-await-in-loop */ + await addContribution(args) + } catch (error) { + console.error( + `Adding ${chalk.blue(contributor.login)} for ${chalk.underline( + contributions, + )} Failed: ${JSON.stringify(error)}`, + ) + } } - process.exit(0) } -function promptForCommand(argv) { +async function promptForCommand(argv) { const questions = [ { type: 'list', @@ -297,9 +313,9 @@ function promptForCommand(argv) { }, ] - return inquirer.prompt(questions).then(answers => { - return answers.command || argv._[0] - }) + const answers = await inquirer.prompt(questions) + + return answers.command || argv._[0] } promptForCommand(yargv) From ba056abd74be0f389e40b4049c03e007bc5549a5 Mon Sep 17 00:00:00 2001 From: Maximilian Berkmann Date: Sun, 24 May 2020 01:32:41 +0100 Subject: [PATCH 3/3] fix(cli): rectify an async call --- src/cli.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli.js b/src/cli.js index 1583514e..daa59fc6 100755 --- a/src/cli.js +++ b/src/cli.js @@ -107,7 +107,7 @@ async function addContribution(argv) { let data try { - data = updateContributors(argv, username, contributions) + data = await updateContributors(argv, username, contributions) } catch (error) { return console.error('Contributor Update fail:', error) }