Skip to content

Notify TSC Members about Voting Status #363

Notify TSC Members about Voting Status

Notify TSC Members about Voting Status #363

Workflow file for this run

name: Notify TSC Members about Voting Status
on:
workflow_dispatch:
inputs:
notification_method:
description: 'Which channel(s) to notify on manual dispatch'
required: true
default: 'both'
type: choice
options:
- email
- slack
- both
schedule:
# Daily at 9:00 UTC
- cron: '0 9 * * *'
jobs:
notify-tsc-members:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# To store the state of the votes and the last time the TSC members were notified
# The format of the file is:
# {
# "issue_number": {
# "status": "open" | "closed",
# "last_notified": "2021-09-01T00:00:00Z"
# }
# }
# Example: https://github.com/ash17290/asyncapi-community/blob/vote_state/vote_status.json
- uses: jorgebg/stateful-action@bd279992190b64c6a5906c3b75a6f2835823ab46
id: state
with:
branch: vote_state
# List all the open issues with the label "vote open"
- name: List current open issues
uses: actions/github-script@v7
id: list
with:
script: |
const { data: issues } = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
labels: 'gitvote/open'
});
return issues;
github-token: ${{ secrets.GITHUB_TOKEN }}
# Fetch the current state from the vote_status.json file
- name: Fetch Current State
id: fetch
run: |
# This file comes from the branch vote_state which is created by the stateful-action
# Eg: https://github.com/ash17290/asyncapi-community/tree/vote_state
cd .vote_state
# Check if the file exists, not empty and readable and create if not
if [ ! -f vote_status.json ] || [ ! -s vote_status.json ] || [ ! -r vote_status.json ]; then
echo "Initializing vote_status.json"
echo "{}" > vote_status.json
fi
# Read the file and export it as JSON
export json=$(cat vote_status.json | jq -c)
echo "::debug::vote_status=$json"
# Store in GitHub Output
echo "vote_status=$json" >> $GITHUB_OUTPUT
# Needed as axios and js-yaml are not available in the default environment
- name: Install the dependencies
run: npm install js-yaml@4.1.0 axios@1.7.4 node-mailjet@6.0.9
shell: bash
- name: Notify TSC Members
uses: actions/github-script@v7
id: notify
env:
MAILJET_API_KEY: ${{ secrets.MAILJET_PUBLIC_KEY}}
MAILJET_API_SECRET: ${{ secrets.MAILJET_PRIVATE_KEY }}
# default to 'both' if inputs.notification_method is undefined (schedule runs)
NOTIFY_METHOD: ${{ github.event.inputs.notification_method || 'both' }}
SLACK_DM: ${{ secrets.SLACK_DM_TSC }}
with:
script: |
const yaml = require('js-yaml');
const fs = require('fs');
const {
filterIssues,
getTSCLeftToVote,
sendSlackNotification,
sendMailNotification
} = require('./.github/workflows/vote-notify-helpers/index.js');
const { sendMail } = require('./.github/workflows/mailing/index.js');
// Creates list of issues for which the TSC members need to be notified
// The number of buffer days is easily configurable by changing the value of the days variable
const issues = ${{ steps.list.outputs.result }};
const initialState = ${{ steps.fetch.outputs.vote_status }};
// On manual dispatch, override to 0; on schedule keep 5
// If workflow_dispatch is used (manual workflow trigger) then we want to set 0 to force notification sending, even if it was just sent or the other day
const daysBuffer = context.eventName === 'workflow_dispatch' ? 0 : 5;
const config = { days: daysBuffer };
const { issuesToNotify, state } = filterIssues(issues, initialState, config);
// Read TSC members from MAINTAINERS.yaml
const tscMembers = yaml.load(fs.readFileSync('MAINTAINERS.yaml', 'utf8')).filter(member => member.isTscMember);
const failingSlackIds = new Set();
const failingEmails = new Set();
const method = process.env.NOTIFY_METHOD; // 'email' | 'slack' | 'both'
console.log(`Notification method: ${method}`);
console.log(`Issues to notify: ${JSON.stringify(issuesToNotify.map(issue => issue.number))}`);
for (const issue of issuesToNotify) {
const { leftToVote, daysSinceStart, voteCommentURL } = await getTSCLeftToVote(issue, tscMembers, github, context);
for (const member of leftToVote) {
console.log(`Notifying ${member.name} about issue #${issue.number}`);
// Only send Slack if requested
if (method === 'slack' || method === 'both') {
if (!await sendSlackNotification(member, issue, daysSinceStart, process.env.SLACK_DM, voteCommentURL)) {
failingSlackIds.add(member.slack);
}
}
// Only send Email if requested
if (method === 'email' || method === 'both') {
if (!await sendMailNotification(member, issue, daysSinceStart, process.env.SLACK_DM, sendMail, voteCommentURL)) {
failingEmails.add(member.email);
}
}
}
// Update last_notified timestamp
state[issue.number].last_notified = new Date().toISOString();
}
// Store the failing IDs in GITHUB_ENV
const failingIds = [...failingSlackIds, ...failingEmails];
if (failingIds.length > 0) {
process.env.FAILED_IDS = failingIds.join(',');
core.exportVariable('FAILED_IDS', JSON.stringify(failingIds));
}
// Store the state back
return JSON.stringify(state);
- name: Update State
if: ${{ steps.notify.outputs.result }}
run: |
echo ${{ steps.notify.outputs.result }} | jq > ./.vote_state/vote_status.json
# Always alert on any failures, regardless of method
- name: Notify about failing Slack DMs
if: ${{ env.FAILED_IDS }}
uses: rtCamp/action-slack-notify@v2
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_CI_FAIL_NOTIFY }}
SLACK_TITLE: 🚨 Vote notifications couldn’t be sent 🚨
SLACK_MESSAGE: ${{ env.FAILED_IDS }}
MSG_MINIMAL: true