• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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