-
-
Notifications
You must be signed in to change notification settings - Fork 100
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
998863c
commit 8b7fb66
Showing
2 changed files
with
185 additions
and
114 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,128 +1,187 @@ | ||
const yaml = require('js-yaml'); | ||
const fs = require('fs'); | ||
const { readFile, writeFile } = require('fs').promises; | ||
const path = require('path'); | ||
|
||
module.exports = async ({ github, context, core }) => { | ||
|
||
const message = context.payload.comment.body; | ||
const eventNumber = context.issue.number; | ||
const eventTitle = context.payload.issue.title | ||
const orgName = context.issue.owner | ||
const repoName = context.issue.repo | ||
|
||
const filePath = path.join('VoteTracking.json'); | ||
|
||
const bindingVotesSectionMatch = message.match(/Binding votes \(\d+\)[\s\S]*?(?=(<details>|$))/); | ||
const bindingVotesSection = bindingVotesSectionMatch ? bindingVotesSectionMatch[0] : ''; | ||
const rows = bindingVotesSection.match(/\| @\w+.*?\|.*?\|.*?\|/g) || []; | ||
const latestVotes = rows.map(row => { | ||
const [, user, vote, timestamp] = row.split('|').map(col => col.trim()); | ||
return { user: user.replace('@', ''), vote, timestamp, isVotedInLast3Months: true }; | ||
}); | ||
|
||
const yamlData = fs.readFileSync('MAINTAINERS.yaml', 'utf8'); | ||
const parsedData = yaml.load(yamlData); | ||
// Initialize vote tracking file if it doesn't exist | ||
if (!fs.existsSync(filePath)) { | ||
const tscMembers = parsedData.filter(entry => entry.isTscMember).map(entry => ({ | ||
name: entry.github, | ||
lastParticipatedVoteTime: '', | ||
isVotedInLast3Months: 'false', | ||
lastVoteClosedTime: new Date().toISOString().split('T')[0], | ||
agreeCount: 0, | ||
disagreeCount: 0, | ||
abstainCount: 0, | ||
notParticipatingCount: 0 | ||
})); | ||
fs.writeFileSync(filePath, JSON.stringify(tscMembers, null, 2)); | ||
} | ||
module.exports = async ({ context }) => { | ||
try { | ||
// Extract necessary details from the context | ||
const message = context.payload.comment.body; | ||
const eventNumber = context.issue.number; | ||
const eventTitle = context.payload.issue.title; | ||
const orgName = context.issue.owner; | ||
const repoName = context.issue.repo; | ||
|
||
// Path to the vote tracking file | ||
const voteTrackingFile = path.join('voteTrackingFile.json'); | ||
|
||
// Parse the vote-closed comment to get voting rows | ||
const votingRows = await parseVoteClosedComment(); | ||
|
||
// Extract latest votes information from the parsed voting rows | ||
const latestVotes = votingRows.map(row => { | ||
const [, user, vote, timestamp] = row.split('|').map(col => col.trim()); | ||
return { user: user.replace('@', ''), vote, timestamp, isVotedInLast3Months: true }; | ||
}); | ||
|
||
// Read and parse the MAINTAINERS.yaml file | ||
const maintainerInfo = await readFile('MAINTAINERS.yaml', 'utf8'); | ||
const maintainerInformation = yaml.load(maintainerInfo); | ||
|
||
// Update the TSC Members if new one added in the community | ||
await updateVoteTrackingFile(); | ||
|
||
// Read and parse the vote tracking file | ||
const voteDetails = JSON.parse(await readFile(voteTrackingFile, 'utf8')); | ||
|
||
const updatedVoteDetails = []; | ||
|
||
const voteDetails = JSON.parse(fs.readFileSync(filePath, 'utf8')); | ||
const latestVotesInfo = [] | ||
voteDetails.map(voteInfo => { | ||
const checkPersonisTSC = parsedData.some(item => item.github === voteInfo.name); | ||
if (checkPersonisTSC) { | ||
const currentTime = new Date().toISOString().split('T')[0]; | ||
const userInfo = latestVotes.find(vote => vote.user === voteInfo.name); | ||
const choice = userInfo ? userInfo.vote : "Not participated"; | ||
|
||
if (userInfo) { | ||
voteInfo.isVotedInLast3Months = true; | ||
voteInfo.lastParticipatedVoteTime = currentTime; | ||
voteInfo[choice === "In favor" ? 'agreeCount' : choice === "Against" ? 'disagreeCount' : 'abstainCount']++; | ||
} else { | ||
voteInfo.notParticipatingCount++; | ||
voteInfo.lastVoteClosedTime = currentTime; | ||
if (!checkVotingDurationMoreThanThreeMonths(voteInfo)) { | ||
voteInfo.isVotedInLast3Months = false; | ||
// Process each vote detail to update voting information | ||
voteDetails.forEach(voteInfo => { | ||
// Checking only valid vote done by TSC Member | ||
const isTscMember = maintainerInformation.some(item => item.github === voteInfo.name); | ||
|
||
if (isTscMember) { | ||
const currentTime = new Date().toISOString().split('T')[0]; | ||
const userInfo = latestVotes.find(vote => vote.user === voteInfo.name); | ||
const voteChoice = userInfo ? userInfo.vote : "Not participated"; | ||
|
||
if (userInfo) { | ||
voteInfo.isVotedInLast3Months = true; | ||
voteInfo.lastParticipatedVoteTime = currentTime; | ||
voteInfo[voteChoice === "In favor" ? 'agreeCount' : voteChoice === "Against" ? 'disagreeCount' : 'abstainCount']++; | ||
} else { | ||
voteInfo.notParticipatingCount++; | ||
voteInfo.lastVoteClosedTime = currentTime; | ||
if (!isVotingWithinLastThreeMonths(voteInfo)) { | ||
voteInfo.isVotedInLast3Months = false; | ||
} | ||
} | ||
|
||
// Update vote information with the issue title and number | ||
// The order is after name the Issue number or PR number should be present | ||
let updatedVoteInfo = {}; | ||
Object.keys(voteInfo).forEach(key => { | ||
if (key === 'name') { | ||
updatedVoteInfo['name'] = voteInfo.name; | ||
updatedVoteInfo[eventTitle + "$$" + eventNumber] = voteChoice; | ||
} else { | ||
updatedVoteInfo[key] = voteInfo[key]; | ||
} | ||
}); | ||
updatedVoteDetails.push(updatedVoteInfo); | ||
} | ||
|
||
let updatedVoteInfo = {}; | ||
Object.keys(voteInfo).forEach(key => { | ||
if (key == 'name') { | ||
updatedVoteInfo['name'] = voteInfo.name | ||
updatedVoteInfo[eventTitle + "$$" + eventNumber] = choice | ||
}); | ||
|
||
// Write the updated vote details back to the file | ||
try { | ||
await writeFile(voteTrackingFile, JSON.stringify(updatedVoteDetails, null, 2)); | ||
} catch (writeError) { | ||
console.error('Error writing to voteTrackingFile.json:', writeError); | ||
} | ||
|
||
// Generate the markdown table and write it to a file | ||
const markdownTable = await jsonToMarkdownTable(updatedVoteDetails); | ||
try { | ||
await writeFile('voteTrackingDetails.md', markdownTable); | ||
console.log('Markdown table has been written to voteTrackingDetails.md'); | ||
} catch (writeError) { | ||
console.error('Error writing to voteTrackingDetails.md:', writeError); | ||
} | ||
|
||
// Functions are defined below | ||
// Convert JSON data to a markdown table | ||
async function jsonToMarkdownTable(data) { | ||
const keys = Object.keys(data[0]); | ||
let markdownTable = '| ' + keys.map(key => { | ||
if (key.includes('$$')) { | ||
const [title, number] = key.split('$$'); | ||
return `[${title}](https://github.com/${orgName}/${repoName}/issues/${number})`; | ||
} | ||
else { | ||
updatedVoteInfo[key] = voteInfo[key]; | ||
const titles = { | ||
name: "GitHub user name", | ||
lastParticipatedVoteTime: "Last time the TSC member participated in a vote", | ||
hasVotedInLast3Months: "Flag indicating if TSC member voted in last 3 months. This information is calculated after each voting, and not basing on a schedule as there might be moments when there is no voting in place for 3 months and therefore no TSC member votes.", | ||
lastVoteClosedTime: "Date when last vote was closed. It indicated when the last voting took place and marks the date when this tracking document was updated.", | ||
agreeCount: "Number of times TSC member agreed in a vote.", | ||
disagreeCount: "Number of times TSC member did not agree in a vote.", | ||
abstainCount: "Number of times TSC member abstained from voting.", | ||
notParticipatingCount: "Number of times TSC member did not participate in voting." | ||
}; | ||
return `<span style="position: relative; cursor: pointer;" title="${titles[key] || key}">${key}</span>`; | ||
}).join(' | ') + ' |\n'; | ||
|
||
markdownTable += '| ' + keys.map(() => '---').join(' | ') + ' |\n'; | ||
markdownTable += data.map(obj => '| ' + keys.map(key => { | ||
if (key === 'name') return `[${obj[key]}](https://github.com/${obj[key]})`; | ||
if (key.includes('$$')) { | ||
const icons = { | ||
"In favor": "👍", | ||
"Against": "👎", | ||
"Abstain": "👀", | ||
"Not participated": "🔕" | ||
}; | ||
return `<span style="position: relative; cursor: pointer;" title="${obj[key]}">${icons[obj[key]] || obj[key]}</span>`; | ||
} | ||
}) | ||
latestVotesInfo.push(updatedVoteInfo) | ||
return obj[key]; | ||
}).join(' | ') + ' |').join('\n'); | ||
|
||
return markdownTable; | ||
} | ||
}); | ||
|
||
fs.writeFileSync(filePath, JSON.stringify(latestVotesInfo, null, 2)); | ||
|
||
// Check voting duration | ||
function checkVotingDurationMoreThanThreeMonths(voteInfo) { | ||
const currentDate = new Date(); | ||
const threeMonthsAgoDate = new Date(currentDate); | ||
threeMonthsAgoDate.setMonth(currentDate.getMonth() - 3); | ||
return new Date(voteInfo.lastVoteClosedTime) >= threeMonthsAgoDate && | ||
new Date(voteInfo.lastParticipatedVoteTime) <= threeMonthsAgoDate; | ||
} | ||
|
||
// Convert JSON data to markdown table | ||
function jsonToMarkdownTable(data) { | ||
const keys = Object.keys(data[0]); | ||
let markdownTable = '| ' + keys.map(key => { | ||
if (key.includes('$$')) { | ||
const [title, number] = key.split('$$'); | ||
return `[${title}](https://github.com/${orgName}/${repoName}/issues/${number})`; | ||
} | ||
const titles = { | ||
name: "GitHub user name", | ||
lastParticipatedVoteTime: "Last time the TSC member participated in a vote", | ||
hasVotedInLast3Months: "Flag indicating if TSC member voted in last 3 months. This information is calculated after each voting, and not basing on a schedule as there might be moments when there is no voting in place for 3 months and therefore no TSC member votes.", | ||
lastVoteClosedTime: "Date when last vote was closed. It indicated when the last voting took place and marks the date when this tracking document was updated.", | ||
agreeCount: "Number of times TSC member agreed in a vote.", | ||
disagreeCount: "Number of times TSC member did not agree in a vote.", | ||
abstainCount: "Number of times TSC member abstained from voting.", | ||
notParticipatingCount: "Number of times TSC member did not participate in voting." | ||
}; | ||
return `<span style="position: relative; cursor: pointer;" title="${titles[key] || key}">${key}</span>`; | ||
}).join(' | ') + ' |\n'; | ||
|
||
markdownTable += '| ' + keys.map(() => '---').join(' | ') + ' |\n'; | ||
markdownTable += data.map(obj => '| ' + keys.map(key => { | ||
if (key === 'name') return `[${obj[key]}](https://github.com/${obj[key]})`; | ||
if (key.includes('$$')) { | ||
const icons = { | ||
"In favor": "👍", | ||
"Against": "👎", | ||
"Abstain": "👀", | ||
"Not participated": "🔕" | ||
}; | ||
return `<span style="position: relative; cursor: pointer;" title="${obj[key]}">${icons[obj[key]] || obj[key]}</span>`; | ||
// Parse the vote-closed comment created by git-vote[bot] | ||
async function parseVoteClosedComment() { | ||
const bindingVotesSectionMatch = message.match(/Binding votes \(\d+\)[\s\S]*?(?=(<details>|$))/); | ||
const bindingVotesSection = bindingVotesSectionMatch ? bindingVotesSectionMatch[0] : ''; | ||
return bindingVotesSection.match(/\| @\w+.*?\|.*?\|.*?\|/g) || []; | ||
} | ||
|
||
// Check if voting duration is within the last three months | ||
function isVotingWithinLastThreeMonths(voteInfo) { | ||
const currentDate = new Date(); | ||
const threeMonthsAgoDate = new Date(currentDate); | ||
threeMonthsAgoDate.setMonth(currentDate.getMonth() - 3); | ||
return new Date(voteInfo.lastVoteClosedTime) >= threeMonthsAgoDate && | ||
new Date(voteInfo.lastParticipatedVoteTime) <= threeMonthsAgoDate; | ||
} | ||
|
||
// Function to update the voteTrackingFile with updated TSC Members | ||
async function updateVoteTrackingFile() { | ||
const tscMembers = maintainerInformation.filter(entry => entry.isTscMember); | ||
let voteDetails = []; | ||
try { | ||
voteDetails = JSON.parse(await readFile(voteTrackingFile, 'utf8')); | ||
} catch (readError) { | ||
console.error('Error reading voteTrackingFile.json:', readError); | ||
} | ||
return obj[key]; | ||
}).join(' | ') + ' |').join('\n'); | ||
|
||
return markdownTable; | ||
const newTSCMembers = []; | ||
|
||
tscMembers.forEach(member => { | ||
const existingMember = voteDetails.find(voteInfo => voteInfo.name === member.github); | ||
if (!existingMember) { | ||
newTSCMembers.push({ | ||
name: member.github, | ||
lastParticipatedVoteTime: '', | ||
isVotedInLast3Months: 'false', | ||
lastVoteClosedTime: new Date().toISOString().split('T')[0], | ||
agreeCount: 0, | ||
disagreeCount: 0, | ||
abstainCount: 0, | ||
notParticipatingCount: 0 | ||
}); | ||
} | ||
}); | ||
if (updatedTSCMembers.length > 0) { | ||
try { | ||
const combinedData = [...voteDetails, ...newTSCMembers]; | ||
await writeFile(voteTrackingFile, JSON.stringify(combinedData, null, 2)); | ||
} catch (writeError) { | ||
console.error('Error writing to voteTrackingFile.json:', writeError); | ||
} | ||
} | ||
} | ||
} catch (error) { | ||
console.error('Error in while running the vote_tracker workflow:', error); | ||
} | ||
|
||
const markdownTable = jsonToMarkdownTable(latestVotesInfo); | ||
fs.writeFileSync('voteTrackingDetails.md', markdownTable); | ||
console.log('Markdown table has been written to voteTrackingDetails.md'); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
[ | ||
{ | ||
"name": "Mayaleeeee", | ||
"lastParticipatedVoteTime": "", | ||
"isVotedInLast3Months": "false", | ||
"lastVoteClosedTime": "2024-07-07", | ||
"agreeCount": 0, | ||
"disagreeCount": 0, | ||
"abstainCount": 0, | ||
"notParticipatingCount": 0 | ||
} | ||
] |