1// this script uses the github api to fetch a list of contributors 2// https://developer.github.com/v3/repos/#list-contributors 3// this endpoint returns a list of contributors sorted by number of contributions 4 5import * as fs from 'fs'; 6import 'isomorphic-fetch'; 7import * as path from 'path'; 8 9const IGNORED_USERS = new Set([ 10 'dependabot[bot]', 11 'eslint[bot]', 12 'greenkeeper[bot]', 13 'semantic-release-bot', 14]); 15 16const COMPLETELY_ARBITRARY_CONTRIBUTION_COUNT = 3; 17const PAGE_LIMIT = 100; 18const contributorsApiUrl = `https://api.github.com/repos/typescript-eslint/typescript-eslint/contributors?per_page=${PAGE_LIMIT}`; 19 20interface Contributor { 21 contributions: number; 22 login: string; 23 url: string; 24} 25interface User { 26 login: string; 27 name: string; 28 avatar_url: string; 29 html_url: string; 30} 31interface AllContributorsUser { 32 login: string; 33 name: string; 34 avatar_url: string; 35 profile: string; 36 contributions: string[]; 37} 38 39async function* fetchUsers(page = 1): AsyncIterableIterator<Contributor[]> { 40 let lastLength = 0; 41 do { 42 const response = await fetch(`${contributorsApiUrl}&page=${page}`, { 43 method: 'GET', 44 }); 45 const contributors: 46 | Contributor[] 47 | { message: string } = await response.json(); 48 49 if (!Array.isArray(contributors)) { 50 throw new Error(contributors.message); 51 } 52 53 const thresholdedContributors = contributors.filter( 54 user => user.contributions >= COMPLETELY_ARBITRARY_CONTRIBUTION_COUNT, 55 ); 56 yield thresholdedContributors; 57 58 lastLength = thresholdedContributors.length; 59 } while ( 60 /* 61 If the filtered list wasn't 100 long, that means that either: 62 - there wasn't 100 users in the page, or 63 - there wasn't 100 users with > threshold commits in the page. 64 65 In either case, it means that there's no need to fetch any more pages 66 */ 67 lastLength === PAGE_LIMIT 68 ); 69} 70 71async function main(): Promise<void> { 72 const githubContributors: Contributor[] = []; 73 74 // fetch all of the contributor info 75 for await (const lastUsers of fetchUsers()) { 76 githubContributors.push(...lastUsers); 77 } 78 79 // fetch the user info 80 const users = await Promise.all( 81 githubContributors.map<Promise<User>>(async c => { 82 const response = await fetch(c.url, { method: 'GET' }); 83 return response.json(); 84 }), 85 ); 86 87 const contributors = users 88 // remove ignored users 89 .filter(u => !IGNORED_USERS.has(u.login)) 90 // fetch the in-depth information for each user 91 .map<AllContributorsUser>(usr => { 92 return { 93 login: usr.login, 94 name: usr.name || usr.login, 95 avatar_url: usr.avatar_url, 96 profile: usr.html_url, 97 contributions: [], 98 }; 99 }); 100 101 // build + write the .all-contributorsrc 102 const allContributorsConfig = { 103 projectName: 'typescript-eslint', 104 projectOwner: 'typescript-eslint', 105 repoType: 'github', 106 repoHost: 'https://github.com', 107 files: ['CONTRIBUTORS.md'], 108 imageSize: 100, 109 commit: false, 110 contributors, 111 contributorsPerLine: 5, 112 }; 113 const rcPath = path.resolve(__dirname, '../.all-contributorsrc'); 114 fs.writeFileSync(rcPath, JSON.stringify(allContributorsConfig, null, 2)); 115} 116 117main(); 118