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

feat: add new worklow to track the voting process #1278

Merged
merged 60 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from 48 commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
422213e
Add new worklow to track the voting process
AayushSaini101 Jun 17, 2024
ab5f4f0
Merge branch 'master' into Issue-1268
AayushSaini101 Jun 17, 2024
dc9e22d
Merge branch 'master' into Issue-1268
AayushSaini101 Jun 19, 2024
63a26df
Updated structure of the worklow
AayushSaini101 Jun 19, 2024
f6853b8
Add extra field to track not participated count
AayushSaini101 Jun 20, 2024
0788db0
Update the names of variables
AayushSaini101 Jun 20, 2024
430f472
Update variables
AayushSaini101 Jun 20, 2024
5cb67c6
Update the vote tracking workflow
AayushSaini101 Jun 20, 2024
9f4c86e
Remove extra variables
AayushSaini101 Jun 20, 2024
82e2d61
Update the tracker concept
AayushSaini101 Jun 20, 2024
cea8957
Update
AayushSaini101 Jun 20, 2024
27917da
update names
AayushSaini101 Jun 20, 2024
171f667
update issue name
AayushSaini101 Jun 20, 2024
1051429
Introduce Markdown to track voting details
AayushSaini101 Jun 21, 2024
597f206
Merge branch 'master' into Issue-1268
AayushSaini101 Jun 21, 2024
07bebfa
Merge branch 'master' into Issue-1268
AayushSaini101 Jun 24, 2024
711d9f6
Add doc about the new workflow and optimise tracker
AayushSaini101 Jun 24, 2024
bfd0988
Use proper variable naming
AayushSaini101 Jun 24, 2024
711b464
Merge branch 'master' into Issue-1268
AayushSaini101 Jun 24, 2024
08c37ab
Update voting.md
AayushSaini101 Jun 24, 2024
7a41ad2
Update .github/scripts/vote_tracker.js
AayushSaini101 Jun 24, 2024
8c12491
Update voting.md
AayushSaini101 Jun 24, 2024
34fcb9b
Update required changes
AayushSaini101 Jun 25, 2024
7f4e172
Merge branch 'master' into Issue-1268
AayushSaini101 Jun 25, 2024
8cb254b
Update to check tsc member
AayushSaini101 Jun 25, 2024
bf6e6e4
Update voting file yml
AayushSaini101 Jun 25, 2024
796386b
remove logging
AayushSaini101 Jun 25, 2024
be7732a
Fix undefined issue
AayushSaini101 Jun 25, 2024
17443f5
Fix the format
AayushSaini101 Jun 25, 2024
901932e
Merge branch 'master' into Issue-1268
AayushSaini101 Jul 1, 2024
7e461c5
Update voting.md
AayushSaini101 Jul 3, 2024
dd19fca
Update voting.md
AayushSaini101 Jul 3, 2024
936f6b0
Update .github/workflows/vote-tracker.yml
AayushSaini101 Jul 3, 2024
541b512
Update the workflow
AayushSaini101 Jul 3, 2024
998863c
Remove extra space
AayushSaini101 Jul 3, 2024
8b7fb66
Update requirments
AayushSaini101 Jul 8, 2024
cde79e9
Merge branch 'master' into Issue-1268
AayushSaini101 Jul 9, 2024
2ce1a67
Update vote_tracker.js
AayushSaini101 Jul 11, 2024
3173ed1
Update vote_tracker.js
AayushSaini101 Jul 17, 2024
daf62e4
Update voteTrackingFile.json
AayushSaini101 Jul 17, 2024
1a0b934
Update .github/scripts/vote_tracker.js
AayushSaini101 Jul 17, 2024
7dcec19
Update vote_tracker.js
AayushSaini101 Jul 17, 2024
f9ddf5a
Merge branch 'master' into Issue-1268
AayushSaini101 Jul 17, 2024
9b822e3
Update voteTrackingFile.json
AayushSaini101 Jul 22, 2024
928ad7e
Update voting.md
AayushSaini101 Jul 22, 2024
944c20e
Merge branch 'master' into Issue-1268
AayushSaini101 Jul 22, 2024
f09efa9
Add markdown file and update the md file generator flow
AayushSaini101 Jul 22, 2024
a2e9222
Update voting.md
AayushSaini101 Jul 22, 2024
348c3a8
Merge branch 'master' into Issue-1268
AayushSaini101 Jul 22, 2024
b03a2be
Fix invalid data
AayushSaini101 Jul 22, 2024
320b543
Fix md file
AayushSaini101 Jul 22, 2024
ddb9272
Update TSC_VOTING_OVERVIEW.md
AayushSaini101 Jul 23, 2024
6c20357
Update voteTrackingFile.json
AayushSaini101 Jul 23, 2024
e96e819
Update TSC_VOTING_OVERVIEW.md
AayushSaini101 Jul 23, 2024
e520dda
Update voteTrackingFile.json
AayushSaini101 Jul 23, 2024
173327a
Update TSC_VOTING_OVERVIEW.md
AayushSaini101 Jul 23, 2024
21f7e76
Update voteTrackingFile.json
AayushSaini101 Jul 23, 2024
8d9dcf1
Merge branch 'master' into Issue-1268
AayushSaini101 Jul 23, 2024
0fc7c38
Add lowerCase matching
AayushSaini101 Jul 23, 2024
cc4e3f8
add lowercase
AayushSaini101 Jul 23, 2024
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
247 changes: 247 additions & 0 deletions .github/scripts/vote_tracker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
const yaml = require('js-yaml');
const { readFile, writeFile } = require('fs').promises;
const path = require('path');

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 created by git-vote[bot]
const votingRows = await parseVoteClosedComment();

// Example table vote comment that is parsed here https://github.com/asyncapi/community/issues/1227#issuecomment-2167463252
const latestVotes = votingRows.map(row => {
//skipping first element as parsing is based on split, so table where column starts with | will have first element of created array empty
const [, user, vote, timestamp] = row.split('|').map(col => col.trim());
return { user: user.replace('@', ''), vote, timestamp, isVotedInLast3Months: true };
});

let maintainerInformation;
try {
const maintainerInfo = await readFile('MAINTAINERS.yaml', 'utf8');
maintainerInformation = yaml.load(maintainerInfo);
} catch (readError) {
console.error('Error reading MAINTAINERS.yaml:', readError);
throw readError;
}

// Update the TSC Members
const voteDetails = await updateVoteTrackingFile();

const updatedVoteDetails = [];

// Process each vote detail to update voting information
voteDetails.forEach(voteInfo => {
const userVote = latestVotes.find(vote => vote.user === voteInfo.name);
let currentTime;
if (userVote && userVote.timestamp) {
currentTime = userVote.timestamp.toString().split(" ")[0];
}
const userInfo = latestVotes.find(vote => vote.user === voteInfo.name);
const voteChoice = userInfo ? userInfo.vote : "Not participated";
voteInfo.lastVoteClosedTime = new Date().toISOString().split('T')[0];

if (userInfo) {
voteInfo.isVotedInLast3Months = true;
voteInfo.lastParticipatedVoteTime = currentTime;
voteInfo[voteChoice === "In favor" ? 'agreeCount' : voteChoice === "Against" ? 'disagreeCount' : 'abstainCount']++;
} else {
voteInfo.notParticipatingCount++;
if (isVotingWithinLastThreeMonths(voteInfo)) {
voteInfo.isVotedInLast3Months = false;
}
}

// Update vote information with the issue title and number
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);
});

try {
await writeFile(voteTrackingFile, JSON.stringify(updatedVoteDetails, null, 2));
} catch (writeError) {
console.error('Error writing to voteTrackingFile.json:', writeError);
throw writeError;
}

const markdownTable = await jsonToMarkdownTable(updatedVoteDetails);
try {
await writeFile('TSC_VOTING_OVERVIEW.md', markdownTable);
console.log('Markdown table has been written to TSC_VOTING_OVERVIEW.md');
} catch (writeError) {
console.error('Error writing to TSC_VOTING_OVERVIEW.md:', writeError);
throw writeError;
}

async function jsonToMarkdownTable(data) {
if (!data || data.length === 0) {
console.error("Data is empty or undefined");
return '';
}

const keys = Object.keys(data[0]).filter(key => key !== 'firstVoteClosedTime');

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."
};

// Fill missing properties with default values and log the processing
data = data.map((obj, index) => {
const newObj = {};
keys.forEach(key => {
newObj[key] = obj[key] !== undefined ? obj[key] : 'N/A';
});
return newObj;
});

let markdownTable = '<!-- This file is generated by a script. Do not manually update it unless there is a visible mistake. -->\n';
markdownTable += '| ' + keys.map(key => {
if (key.includes('$$')) {
const [title, number] = key.split('$$');
return `[${title}](https://github.com/${orgName}/${repoName}/issues/${number})`;
}
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>`;
}
return obj[key];
}).join(' | ') + ' |').join('\n');

return markdownTable;
}


// Parse the vote-closed comment created by git-vote[bot]
AayushSaini101 marked this conversation as resolved.
Show resolved Hide resolved
// No need to look for "Vote closed" as this is already validated by the workflow that runs this code
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();
let previousDate;
if (voteInfo.isVotedInLast3Months === "Member has not voted in all previous voting process.") {
previousDate = new Date(voteInfo.firstVoteClosedTime);
} else {
previousDate = new Date(voteDetails.lastVoteClosedTime);
}
const yearDiff = currentDate.getFullYear() - previousDate.getFullYear();
const monthDiff = currentDate.getMonth() - previousDate.getMonth();
const totalMonthsDiff = yearDiff * 12 + monthDiff;

return totalMonthsDiff > 3;
}

// 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);
throw readError;
}
const updatedTSCMembers = [];
const requiredKeys = ['name', 'lastParticipatedVoteTime', 'isVotedInLast3Months', 'lastVoteClosedTime', 'agreeCount', 'disagreeCount', 'abstainCount', 'notParticipatingCount'];
// Function to check if an object has all required keys
const isValidExampleMember = (member) => {
return requiredKeys.every(key => member.hasOwnProperty(key));
};
// Find the first valid example member
const validExampleMember = voteDetails.find(isValidExampleMember);

if (validExampleMember) {
tscMembers.forEach(member => {
const existingMember = voteDetails.find(voteInfo => voteInfo.name === member.github);
if (!existingMember) {
// Create a new member by copying the structure of the valid example member
const newMember = {};

// Copy the keys from the valid example member to the new member with default values
Object.keys(validExampleMember).forEach(key => {
switch (key) {
case 'name':
newMember[key] = member.github;
break;
case 'lastParticipatedVoteTime':
newMember[key] = 'Member has not participated in all previous voting process.';
break;
case 'isVotedInLast3Months':
newMember[key] = 'Member has not participated in all previous voting process.';
break;
case 'lastVoteClosedTime':
newMember[key] = new Date().toISOString().split('T')[0];
break;
case 'firstVoteClosedTime':
newMember[key] = validExampleMember['firstVoteClosedTime']; // This is used to determine when the first vote closed so that we can determine the duration between two votes easily
break;
case 'agreeCount':
case 'disagreeCount':
case 'abstainCount':
case 'notParticipatingCount':
newMember[key] = 0;
break;
default:
newMember[key] = "Not participated";
}
});

updatedTSCMembers.push(newMember);
}
});
} else {
console.log('No valid example member found in voteDetails.');
}

if (updatedTSCMembers.length > 0) {
try {
const combinedData = [...voteDetails, ...updatedTSCMembers];
await writeFile(voteTrackingFile, JSON.stringify(combinedData, null, 2));
return combinedData; // Return the updated data
} catch (writeError) {
console.error('Error wile writing file:' ,writeError)
}
}
}
} catch (error) {
console.error('Error while running the vote_tracker workflow:', error);
}
}
37 changes: 37 additions & 0 deletions .github/workflows/vote-tracker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Vote Tracker

on:
issue_comment:
types: [created]
derberg marked this conversation as resolved.
Show resolved Hide resolved

jobs:
track-vote:
if: ${{ github.actor == 'git-vote[bot]' && contains(github.event.comment.body, 'Vote closed')}}
runs-on: ubuntu-latest
steps:

- name: Checkout repository
uses: actions/checkout@v4

- name: Installing Module
run: npm install js-yaml@4.1.0
shell: bash

- name: Run GitHub Script
id: vote_tracker
uses: actions/github-script@v7
with:
script: |
const script = require('./.github/scripts/vote_tracker.js');
await script({ github, context, core });

- name: Create Pull Request to update Vote Tracking Details
uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38 # uses 5.0.2 https://github.com/peter-evans/create-pull-request/releases/tag/v5.0.2
with:
token: ${{ secrets.GH_TOKEN }}
commit-message: 'chore: update vote tracking details'
committer: asyncapi-bot <info@asyncapi.io>
author: asyncapi-bot <info@asyncapi.io>
title: 'chore: vote tracking details'
body: 'Update the votetrackingDetails.md and votetracking.json'
branch: vote-trackingupdate/${{ github.job }}
Loading
Loading