| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent f46ae3f commit 61ec594
1 file changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,95 @@ | |||
| 1 | + #!/usr/bin/env node | ||
| 2 | + | ||
| 3 | + // Identify inactive collaborators. "Inactive" is not quite right, as the things | ||
| 4 | + // this checks for are not the entirety of collaborator activities. Still, it is | ||
| 5 | + // a pretty good proxy. Feel free to suggest or implement further metrics. | ||
| 6 | + | ||
| 7 | + import cp from 'node:child_process'; | ||
| 8 | + import fs from 'node:fs'; | ||
| 9 | + import readline from 'node:readline'; | ||
| 10 | + | ||
| 11 | + const SINCE = process.argv[2] || '6 months ago'; | ||
| 12 | + | ||
| 13 | + async function runGitCommand(cmd, mapFn) { | ||
| 14 | + const childProcess = cp.spawn('/bin/sh', ['-c', cmd], { | ||
| 15 | + cwd: new URL('..', import.meta.url), | ||
| 16 | + encoding: 'utf8', | ||
| 17 | + stdio: ['inherit', 'pipe', 'inherit'], | ||
| 18 | + }); | ||
| 19 | + const lines = readline.createInterface({ | ||
| 20 | + input: childProcess.stdout, | ||
| 21 | + }); | ||
| 22 | + const errorHandler = new Promise( | ||
| 23 | + (_, reject) => childProcess.on('error', reject) | ||
| 24 | + ); | ||
| 25 | + const returnedSet = new Set(); | ||
| 26 | + await Promise.race([errorHandler, Promise.resolve()]); | ||
| 27 | + for await (const line of lines) { | ||
| 28 | + await Promise.race([errorHandler, Promise.resolve()]); | ||
| 29 | + const val = mapFn(line); | ||
| 30 | + if (val) { | ||
| 31 | + returnedSet.add(val); | ||
| 32 | + } | ||
| 33 | + } | ||
| 34 | + return Promise.race([errorHandler, Promise.resolve(returnedSet)]); | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + // Get all commit authors during the time period. | ||
| 38 | + const authors = await runGitCommand( | ||
| 39 | + `git shortlog -n -s --since="${SINCE}"`, | ||
| 40 | + (line) => line.trim().split('\t', 2)[1] | ||
| 41 | + ); | ||
| 42 | + | ||
| 43 | + // Get all commit landers during the time period. | ||
| 44 | + const landers = await runGitCommand( | ||
| 45 | + `git shortlog -n -s -c --since="${SINCE}"`, | ||
| 46 | + (line) => line.trim().split('\t', 2)[1] | ||
| 47 | + ); | ||
| 48 | + | ||
| 49 | + // Get all approving reviewers of landed commits during the time period. | ||
| 50 | + const approvingReviewers = await runGitCommand( | ||
| 51 | + `git log --since="${SINCE}" | egrep "^ Reviewed-By: "`, | ||
| 52 | + (line) => /^ Reviewed-By: ([^<]+)/.exec(line)[1].trim() | ||
| 53 | + ); | ||
| 54 | + | ||
| 55 | + async function retrieveCollaboratorsFromReadme() { | ||
| 56 | + const readmeText = readline.createInterface({ | ||
| 57 | + input: fs.createReadStream(new URL('../README.md', import.meta.url)), | ||
| 58 | + crlfDelay: Infinity, | ||
| 59 | + }); | ||
| 60 | + const returnedArray = []; | ||
| 61 | + let processingCollaborators = false; | ||
| 62 | + for await (const line of readmeText) { | ||
| 63 | + const isCollaborator = processingCollaborators && line.length; | ||
| 64 | + if (line === '### Collaborators') { | ||
| 65 | + processingCollaborators = true; | ||
| 66 | + } | ||
| 67 | + if (line === '### Collaborator emeriti') { | ||
| 68 | + processingCollaborators = false; | ||
| 69 | + break; | ||
| 70 | + } | ||
| 71 | + if (line.startsWith('**') && isCollaborator) { | ||
| 72 | + returnedArray.push(line.split('**', 2)[1].trim()); | ||
| 73 | + } | ||
| 74 | + } | ||
| 75 | + return returnedArray; | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + // Get list of current collaborators from README.md. | ||
| 79 | + const collaborators = await retrieveCollaboratorsFromReadme(); | ||
| 80 | + | ||
| 81 | + console.log(`${authors.size.toLocaleString()} authors have made commits since ${SINCE}.`); | ||
| 82 | + console.log(`${landers.size.toLocaleString()} landers have landed commits since ${SINCE}.`); | ||
| 83 | + console.log(`${approvingReviewers.size.toLocaleString()} reviewers have approved landed commits since ${SINCE}.`); | ||
| 84 | + console.log(`${collaborators.length.toLocaleString()} collaborators currently in the project.`); | ||
| 85 | + | ||
| 86 | + const inactive = collaborators.filter((collaborator) => | ||
| 87 | + !authors.has(collaborator) && | ||
| 88 | + !landers.has(collaborator) && | ||
| 89 | + !approvingReviewers.has(collaborator) | ||
| 90 | + ); | ||
| 91 | + | ||
| 92 | + if (inactive.length) { | ||
| 93 | + console.log('\nInactive collaborators:'); | ||
| 94 | + console.log(inactive.join('\n')); | ||
| 95 | + } | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments