mirror of
https://github.com/all-contributors/cli.git
synced 2025-01-09 21:46:29 +00:00
* fix: Adjusting file access that was clashing with uncontrolled promises * review: Adding code review changing and linting * fix(cli): rectify an async call Co-authored-by: Maximilian Berkmann <maxieberkmann@gmail.com>
This commit is contained in:
parent
feefb2aac2
commit
f54703d0ce
5 changed files with 212 additions and 202 deletions
|
@ -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,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
"homepage": "https://github.com/all-contributors/all-contributors-cli#readme",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.7.6",
|
||||
"ac-learn": "^1.5.1",
|
||||
"async": "^3.1.0",
|
||||
"chalk": "^4.0.0",
|
||||
"ac-learn": "^1.0.3",
|
||||
|
@ -51,7 +52,7 @@
|
|||
"inquirer": "^7.3.3",
|
||||
"json-fixer": "^1.6.8",
|
||||
"lodash": "^4.11.2",
|
||||
"name-your-contributors": "^3.4.0",
|
||||
"name-your-contributors": "^3.8.3",
|
||||
"node-fetch": "^2.6.0",
|
||||
"pify": "^5.0.0",
|
||||
"yargs": "^15.0.1",
|
||||
|
|
356
src/cli.js
356
src/cli.js
|
@ -12,7 +12,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')
|
||||
|
@ -51,10 +51,26 @@ 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}`))
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -63,7 +79,7 @@ function startGeneration(argv) {
|
|||
)
|
||||
}
|
||||
|
||||
function addContribution(argv) {
|
||||
async function addContribution(argv) {
|
||||
util.configFile.readConfig(argv.config) // ensure the config file exists
|
||||
const username = argv._[1] === undefined ? undefined : String(argv._[1])
|
||||
/* Example: (for clarity & debugging purposes)
|
||||
|
@ -85,204 +101,188 @@ function addContribution(argv) {
|
|||
}
|
||||
*/
|
||||
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 = await 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,
|
||||
const repoContributors = await repo.getContributors(
|
||||
configData.projectOwner,
|
||||
configData.projectName,
|
||||
configData.repoType,
|
||||
configData.repoHost,
|
||||
)
|
||||
|
||||
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 missingInConfig = repoContributors.filter(
|
||||
key => !knownContributors.includes(key),
|
||||
)
|
||||
const missingFromRepo = knownContributors.filter(key => {
|
||||
return (
|
||||
!repoContributors.includes(key) &&
|
||||
(knownContributions[key].includes('code') ||
|
||||
knownContributions[key].includes('test'))
|
||||
)
|
||||
.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
|
||||
return obj
|
||||
}, {})
|
||||
const knownContributors = configData.contributors.map(
|
||||
contributor => contributor[checkKey],
|
||||
)
|
||||
})
|
||||
|
||||
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 (missingInConfig.length) {
|
||||
process.stdout.write(
|
||||
chalk.bold('Missing contributors in .all-contributorsrc:\n'),
|
||||
)
|
||||
process.stdout.write(` ${missingInConfig.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`)
|
||||
}
|
||||
})
|
||||
if (missingFromRepo.length) {
|
||||
process.stdout.write(
|
||||
chalk.bold('Unknown contributors found in .all-contributorsrc:\n'),
|
||||
)
|
||||
process.stdout.write(`${missingFromRepo.join(', ')}\n`)
|
||||
}
|
||||
}
|
||||
|
||||
function fetchContributors(argv) {
|
||||
// console.log('argv=', argv);
|
||||
// const configData = util.configFile.readConfig(argv.config)
|
||||
// console.log('configData')
|
||||
// console.dir(configData)
|
||||
async function fetchContributors(argv) {
|
||||
const {reviewers, commitAuthors, issueCreators} = await getContributors(
|
||||
argv.projectOwner,
|
||||
argv.projectName,
|
||||
)
|
||||
const args = {...argv, _: []}
|
||||
const contributorsToAdd = []
|
||||
const learner = await getLearner()
|
||||
|
||||
return getContributors(argv.projectOwner, argv.projectName).then(
|
||||
repoContributors => {
|
||||
// repoContributors = {prCreators, prCommentators, issueCreators, issueCommentators, reviewers, commitAuthors, commitCommentators}
|
||||
// console.dir(repoContributors)
|
||||
reviewers.forEach(usr => {
|
||||
contributorsToAdd.push({login: usr.login, contributions: ['review']})
|
||||
|
||||
// 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', ...]
|
||||
console.log(
|
||||
`Adding ${chalk.underline('Reviewer')} ${chalk.blue(usr.login)}`,
|
||||
)
|
||||
})
|
||||
|
||||
// let contributors = new Set(
|
||||
// repoContributors.prCreators.map(usr => usr.login),
|
||||
// )
|
||||
issueCreators.forEach(usr => {
|
||||
const contributor = {
|
||||
login: usr.login,
|
||||
contributions: [],
|
||||
}
|
||||
|
||||
// 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)
|
||||
usr.labels.forEach(lbl => {
|
||||
const guessedCategory = learner
|
||||
.classify(lbl)
|
||||
.find(ctr => ctr && ctr !== 'null')
|
||||
|
||||
// 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)}`,
|
||||
// )
|
||||
})
|
||||
|
||||
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 (!guessedCategory) {
|
||||
console.warn(
|
||||
`Oops, I couldn't find any category for the "${lbl}" label`,
|
||||
)
|
||||
if (existingContributor.length) {
|
||||
existingContributor[0].contributions = [
|
||||
...new Set(
|
||||
existingContributor[0].contributions.concat(
|
||||
contributor.contributions,
|
||||
),
|
||||
),
|
||||
]
|
||||
} else contributorsToAdd.push(contributor)
|
||||
})
|
||||
|
||||
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 (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']})
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (!contributor.contributions.includes(guessedCategory)) {
|
||||
contributor.contributions.push(guessedCategory)
|
||||
|
||||
// console.log('contributorsToAdd=', contributorsToAdd)
|
||||
contributorsToAdd.forEach(contributor => {
|
||||
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),
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function onError(error) {
|
||||
if (error) {
|
||||
console.error(error.stack || error.message || error)
|
||||
process.exit(1)
|
||||
const existingContributor = contributorsToAdd.find(
|
||||
ctr => ctr.login === usr.login,
|
||||
)
|
||||
|
||||
if (existingContributor) {
|
||||
existingContributor.contributions.push(...contributor.contributions)
|
||||
} else {
|
||||
contributorsToAdd.push(contributor)
|
||||
}
|
||||
})
|
||||
|
||||
commitAuthors.forEach(usr => {
|
||||
const existingContributor = contributorsToAdd.find(
|
||||
ctr => ctr.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)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// Format contributor contributions
|
||||
const contributions = contributor.contributions.join('/')
|
||||
|
||||
console.log(
|
||||
`Adding ${chalk.blue(contributor.login)} for ${chalk.underline(
|
||||
contributions,
|
||||
)}`,
|
||||
)
|
||||
|
||||
args._ = ['', contributor.login, contributor.contributions.join(',')]
|
||||
|
||||
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',
|
||||
|
@ -312,9 +312,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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
/* eslint-enable no-console */
|
||||
async function getLearner() {
|
||||
const learner = new Learner()
|
||||
|
||||
module.exports = 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
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getLearner,
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue