-
Notifications
You must be signed in to change notification settings - Fork 5.1k
/
Copy pathgenerate-rc-commits.js
217 lines (189 loc) · 6.16 KB
/
generate-rc-commits.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
const fs = require('fs');
const simpleGit = require('simple-git');
const { Octokit } = require('@octokit/core');
const octokit = new Octokit({
auth: 'your-access-token',
});
/**
* This script is used to filter and group commits by teams based on unique commit messages.
* It takes two branches as input and generates a CSV file with the commit message, author,PR link, team,release tag and commit hash
* The teams and their members are defined in the 'teams.json' file.
*
* Command to run the script: node development/generate-rc-commits.js origin/branchA origin/branchB
*
* @example <caption> Sample command to get all the commits from release v11.13.0 to v11.14.0 </caption>
* node development/generate-rc-commits.js origin/Version-v11.14.0 origin/Version-v11.13.0
* Output: the generated commits will be in a file named 'commits.csv'.
*/
// Function to fetch author teams mapping file from teams.json
async function fetchAuthorTeamsFile() {
try {
const { data } = await octokit.request(
'GET /repos/{owner}/{repo}/contents/{path}',
{
owner: 'MetaMask',
repo: 'MetaMask-planning',
path: 'teams.json',
},
);
const content = Buffer.from(data.content, 'base64').toString('utf-8');
return JSON.parse(content); // Assuming the file is in JSON format
} catch (error) {
console.error('Error fetching author teams mapping file:', error);
return {};
}
}
// Function to get PR labels
async function getPRLabels(owner, repo, prNumber) {
try {
const response = await octokit.request(
'GET /repos/{owner}/{repo}/issues/{issue_number}/labels',
{
owner,
repo,
issue_number: prNumber,
},
);
return response.data.map((label) => label.name);
} catch (error) {
console.error('Error fetching PR labels:', error);
return {};
}
}
// Function to get the GitHub username for a given commit hash
async function getGitHubUsername(commitHash) {
try {
const { data } = await octokit.request(
'GET /repos/{owner}/{repo}/commits/{ref}',
{
owner: 'MetaMask',
repo: 'metamask-extension',
ref: commitHash,
},
);
return data.author ? data.author.login : null;
} catch (error) {
console.error('Error fetching GitHub username:', error);
return null;
}
}
// Function to filter commits based on unique commit messages and group by teams
async function filterCommitsByTeam(branchA, branchB, authorTeams) {
try {
const git = simpleGit();
const logOptions = {
from: branchB,
to: branchA,
format: {
hash: '%H',
author: '%an',
message: '%s',
},
};
const log = await git.log(logOptions);
const seenMessages = new Set();
const commitsByTeam = {};
let processedCommits = 0;
const MAX_COMMITS = 500; // Limit the number of commits to process
console.log('Generation of the CSV file "commits.csv" is in progress...');
for (const commit of log.all) {
if (processedCommits >= MAX_COMMITS) {
break;
}
const { author, message, hash } = commit;
const githubUsername = await getGitHubUsername(hash);
let team = authorTeams[githubUsername] || 'Other/Unknown';
// Format the team label
team = team
.replace(/^team-/u, '') // Remove the "team-" prefix
.split('-') // Split the string into an array of words
.map((word) => word.charAt(0).toUpperCase() + word.slice(1)) // Capitalize the first letter of each word
.join(' '); // Join the words back into a string with spaces
// Extract PR number from the commit message using regex
const prMatch = message.match(/\(#(\d+)\)/u);
const prLink = prMatch
? `https://github.com/MetaMask/metamask-extension/pull/${prMatch[1]}`
: '';
// Check if the commit message is unique and exclude 'Changelog' or 'Merge pull request' or 'master-sync' in the message
if (
!seenMessages.has(message) &&
prMatch &&
!message.includes('changelog') &&
!message.includes('Merge pull request') &&
!message.includes('master-sync')
) {
const labels = await getPRLabels(
'MetaMask',
'metamask-extension',
prMatch[1],
);
const filteredLabels = labels.filter((label) =>
label.includes('release'),
);
const releaseLabel = filteredLabels[0];
seenMessages.add(message);
// Initialize the team's commits array if it doesn't exist
if (!commitsByTeam[team]) {
commitsByTeam[team] = [];
}
commitsByTeam[team].push({
message,
author,
prLink,
releaseLabel,
hash: hash.substring(0, 10),
});
processedCommits += 1;
}
}
return commitsByTeam;
} catch (error) {
console.error(error);
return {};
}
}
function formatAsCSV(commitsByTeam) {
const csvContent = [];
for (const [team, commits] of Object.entries(commitsByTeam)) {
commits.forEach((commit) => {
commit.message = commit.message.replace(/,/gu, ''); // Remove commas from the commit message
const row = [
commit.message,
commit.author,
commit.prLink,
team,
commit.releaseLabel,
commit.hash,
];
csvContent.push(row.join(','));
});
}
csvContent.unshift(
'Commit Message,Author,PR Link,Team,Release Label,Commit Hash',
);
return csvContent;
}
async function main() {
const args = process.argv.slice(2);
if (args.length !== 2) {
console.error('Usage: node script.js branchA branchB');
process.exit(1);
}
const branchA = args[0];
const branchB = args[1];
// Fetch author teams mapping from the teams.json file
const authorTeams = await fetchAuthorTeamsFile();
const commitsByTeam = await filterCommitsByTeam(
branchA,
branchB,
authorTeams,
);
if (Object.keys(commitsByTeam).length === 0) {
console.log('No unique commits found.');
} else {
const csvContent = formatAsCSV(commitsByTeam);
fs.writeFileSync('commits.csv', csvContent.join('\n'));
console.log('CSV file "commits.csv" created successfully.');
}
}
main();