Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: Centralize Author/Team Mapping for Commit Tracking #25986

Merged
merged 8 commits into from
Jul 24, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 58 additions & 105 deletions development/generate-rc-commits.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const octokit = new Octokit({
/**
* 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 'authorTeams' object.
* 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
*
Expand All @@ -19,96 +19,24 @@ const octokit = new Octokit({
* Output: the generated commits will be in a file named 'commits.csv'.
*/

// JSON mapping authors to teams
const authorTeams = {
Accounts: [
'Owen Craston',
'Gustavo Antunes',
'Monte Lai',
'Daniel Rocha',
'Howard Braham',
'Kate Johnson',
'Xiaoming Wang',
'Charly Chevalier',
'Mike B',
],
'Wallet UX': ['David Walsh', 'Nidhi Kumari', 'Jony Bursztyn'],
'Extension Platform': [
'chloeYue',
'Chloe Gao',
'danjm',
'Danica Shen',
'Brad Decker',
'hjetpoluru',
'Harika Jetpoluru',
'Marina Boboc',
'Gauthier Petetin',
'Dan Miller',
'Dan J Miller',
'David Murdoch',
'Niranjana Binoy',
'Victor Thomas',
'vthomas13',
'seaona',
'Norbert Elter',
],
'Wallet API': ['tmashuang', 'jiexi', 'BelfordZ', 'Shane'],
Confirmations: [
'Pedro Figueiredo',
'Sylva Elendu',
'Olusegun Akintayo',
'Jyoti Puri',
'Ariella Vu',
'OGPoyraz',
'vinistevam',
'Matthew Walsh',
'cryptotavares',
'Vinicius Stevam',
'Derek Brans',
'sleepytanya',
'Priya',
],
'Design Systems': [
'georgewrmarshall',
'Garrett Bear',
'George Marshall',
'Devin',
],
Snaps: [
'David Drazic',
'hmalik88',
'Montoya',
'Mrtenz',
'Frederik Bolding',
'Bowen Sanders',
'Guillaume Roux',
'Hassan Malik',
'Maarten Zuidhoorn',
'Jonathan Ferreira',
],
Assets: ['salimtb', 'sahar-fehri', 'Brian Bergeron'],
Linea: ['VGau', 'Victorien Gauch'],
lavamoat: ['weizman', 'legobeat', 'kumavis', 'LeoTM'],
'Wallet Framework': [
'Michele Esposito',
'Elliot Winkler',
'Gudahtt',
'Jongsun Suh',
'Mark Stacey',
],
MMI: [
'António Regadas',
'Albert Olivé',
'Ramon AC',
'Shane T',
'Bernardo Garces Chapero',
],
Swaps: ['Daniel', 'Davide Brocchetto', 'Nicolas Ferro', 'infiniteflower'],
Devex: ['Thomas Huang', 'Alex Donesky', 'jiexi', 'Zachary Belford'],
Notifications: ['Prithpal-Sooriya', 'Matteo Scurati', 'Prithpal Sooriya'],
Bridging: ['Bilal', 'micaelae', 'Ethan Wessel'],
Ramps: ['George Weiler'],
};
// 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) {
Expand All @@ -129,18 +57,26 @@ async function getPRLabels(owner, repo, prNumber) {
}
}

// Function to get the team for a given author
function getTeamForAuthor(authorName) {
for (const [team, authors] of Object.entries(authorTeams)) {
if (authors.includes(authorName)) {
return team;
}
// Function to get the GitHub username for a given commit hash
async function getGitHubUsername(commitHash) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the teams.json file, the PR author's username (handle) is used, so we need to retrieve the username instead of the author's name from the commit log.

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;
}
return 'Other/Unknown'; // Default team for unknown authors
}

// Function to filter commits based on unique commit messages and group by teams
async function filterCommitsByTeam(branchA, branchB) {
async function filterCommitsByTeam(branchA, branchB, authorTeams) {
try {
const git = simpleGit();

Expand All @@ -157,17 +93,27 @@ async function filterCommitsByTeam(branchA, branchB) {
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) {
const { author, message, hash } = commit;
if (commitsByTeam.length >= MAX_COMMITS) {
if (processedCommits >= MAX_COMMITS) {
Copy link
Contributor

@chloeYue chloeYue Jul 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested and found the current commitsByTeam.length >= MAX_COMMITSdoes not work because commitsByTeam is a object, not a Map, so it doesn't have a size property. We should track the number of processed commits by processedCommits.

break;
}

const team = getTeamForAuthor(author);
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);
Expand Down Expand Up @@ -206,9 +152,9 @@ async function filterCommitsByTeam(branchA, branchB) {
releaseLabel,
hash: hash.substring(0, 10),
});
processedCommits += 1;
}
}

return commitsByTeam;
} catch (error) {
console.error(error);
Expand All @@ -233,7 +179,7 @@ function formatAsCSV(commitsByTeam) {
});
}
csvContent.unshift(
'Commit Message,Author,PR Link,Team,Release Label, Commit Hash',
'Commit Message,Author,PR Link,Team,Release Label,Commit Hash',
);

return csvContent;
Expand All @@ -250,7 +196,14 @@ async function main() {
const branchA = args[0];
const branchB = args[1];

const commitsByTeam = await filterCommitsByTeam(branchA, branchB);
// 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.');
Expand Down