#!/usr/bin/env node // Attribution: // This file is based on https://github.com/nodejs/node/blob/master/tools/update-authors.js. // It is licensed according to https://github.com/nodejs/node/blob/master/LICENSE // Usage: tools/update-author.js [--dry] // Passing --dry will redirect output to stdout rather than write to 'AUTHORS'. 'use strict' const { spawn } = require('child_process') const path = require('path') const fs = require('fs') const readline = require('readline') class CaseIndifferentMap { constructor () { this._map = new Map() } get (key) { return this._map.get(key.toLowerCase()) } set (key, value) { return this._map.set(key.toLowerCase(), value) } } const log = spawn( 'git', // Inspect author name/email and body. ['log', '--reverse', '--format=Author: %aN <%aE>\n%b'], { stdio: ['inherit', 'pipe', 'inherit'] }) const rl = readline.createInterface({ input: log.stdout }) let output if (process.argv.includes('--dry')) { output = process.stdout } else { output = fs.createWriteStream('AUTHORS') } output.write('# Authors ordered by first contribution.\n\n') const mailmap = new CaseIndifferentMap() { const lines = fs.readFileSync(path.resolve(__dirname, '../', '.mailmap'), { encoding: 'utf8' }).split('\n') for (let line of lines) { line = line.trim() if (line.startsWith('#') || line === '') continue // Replaced Name const match1 = line.match(/^([^<]+)\s+(<[^>]+>)$/) if (match1) { mailmap.set(match1[2], { author: match1[1] }) continue } // const match2 = line.match(/^<([^>]+)>\s+(<[^>]+>)$/) if (match2) { mailmap.set(match2[2], { email: match2[1] }) continue } // Replaced Name const match3 = line.match(/^([^<]+)\s+(<[^>]+>)\s+(<[^>]+>)$/) if (match3) { mailmap.set(match3[3], { author: match3[1], email: match3[2] }) continue } // Replaced Name Original Name const match4 = line.match(/^([^<]+)\s+(<[^>]+>)\s+([^<]+)\s+(<[^>]+>)$/) if (match4) { mailmap.set(match4[3] + '\0' + match4[4], { author: match4[1], email: match4[2] }) continue } console.warn('Unknown .mailmap format:', line) } } const seen = new Set() // Support regular git author metadata, as well as `Author:` and // `Co-authored-by:` in the message body. Both have been used in the past // to indicate multiple authors per commit, with the latter standardized // by GitHub now. const authorRe = /(^Author:|^Co-authored-by:)\s+(?[^<]+)\s+(?<[^>]+>)/i rl.on('line', (line) => { const match = line.match(authorRe) if (!match) return const { author, email } = match.groups if (seen.has(email) || /@chromium\.org/.test(email) || /greenkeeper\[bot\]/.test(email) || email === '') { return } seen.add(email) output.write(`${author} ${email}\n`) }) rl.on('close', () => { output.end('\n# Generated by tools/update-authors.js\n') })