Skip to content

PQ Interop #9, September 10, 2025 #178

PQ Interop #9, September 10, 2025

PQ Interop #9, September 10, 2025 #178

name: Protocol Call Handler
on:
issues:
types: [opened, edited]
jobs:
check_label:
runs-on: ubuntu-latest
outputs:
has_protocol_label: ${{ steps.check_label.outputs.has_protocol_label }}
steps:
- name: Check for protocol-call label
id: check_label
run: |
if [[ "${{ contains(github.event.issue.labels.*.name, 'protocol-call') }}" == "true" ]]; then
echo "has_protocol_label=true" >> $GITHUB_OUTPUT
echo "✅ Issue has protocol-call label"
else
echo "has_protocol_label=false" >> $GITHUB_OUTPUT
echo "❌ Issue does not have protocol-call label"
fi
check_membership:
needs: check_label
runs-on: ubuntu-latest
if: needs.check_label.outputs.has_protocol_label == 'true'
outputs:
is_member: ${{ steps.set_output.outputs.is_member }}
steps:
- name: Check Organization Membership
id: check_org_membership
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
// Check if the user is a member of ethereum or ethcatherders orgs
let is_member = false;
const username = context.actor;
// Define trusted contributors list once at the beginning
const trustedContributors = [
"poojaranjan", "adietrichs", "carlbeek", "timbeiko", "nconsigny",
"justindrake", "vbuterin", "hwwhww", "mathhhewkeil", "jrudolf",
"soispoke", "pipermerriam", "potuz", "will-corcoran", "ralexstokes",
"samwilsn", "shemnon", "nixorokish", "dcrapis", "gabrocheleau",
"JoshDavisLight", "nerolation", "jihoonsong"
];
console.log(`Checking membership for user: ${username}`);
try {
// Try multiple methods to check membership to be thorough
// First, try direct membership check - this only works for public members or if token has sufficient permissions
async function checkDirectMembership(org, user) {
try {
await github.rest.orgs.checkMembershipForUser({
org: org,
username: user
});
return true;
} catch (error) {
console.log(`Direct membership check for ${user} in ${org} failed: ${error.message}`);
return false;
}
}
// Next, try to get both public and private members
async function getAllOrgMembers(org) {
const members = new Set();
const perPage = 100; // Maximum allowed
// Get public members
try {
console.log(`Fetching public members for ${org}...`);
for await (const response of github.paginate.iterator(
github.rest.orgs.listPublicMembers,
{ org, per_page: perPage }
)) {
for (const member of response.data) {
members.add(member.login.toLowerCase());
}
}
console.log(`Found ${members.size} public members in ${org}`);
} catch (error) {
console.log(`Error fetching public members for ${org}: ${error.message}`);
}
// Get all members (includes private, if token has permissions)
try {
console.log(`Fetching all members for ${org}...`);
for await (const response of github.paginate.iterator(
github.rest.orgs.listMembers,
{ org, per_page: perPage }
)) {
for (const member of response.data) {
members.add(member.login.toLowerCase());
}
}
console.log(`Found total of ${members.size} members in ${org} (public + private that token can see)`);
} catch (error) {
console.log(`Error fetching all members for ${org}: ${error.message}`);
}
return Array.from(members);
}
// Check Ethereum org
console.log('Checking ethereum organization membership...');
if (await checkDirectMembership('ethereum', username)) {
console.log(`✅ ${username} is confirmed as a member of ethereum org via direct API check`);
is_member = true;
} else {
const ethereumMembers = await getAllOrgMembers('ethereum');
console.log(`Collected ${ethereumMembers.length} ethereum members to check against`);
if (ethereumMembers.includes(username.toLowerCase())) {
console.log(`✅ ${username} is a member of ethereum org`);
is_member = true;
} else {
console.log(`❌ ${username} is not found in ethereum org member list`);
}
}
// If not in ethereum, check ethcatherders org
if (!is_member) {
console.log('Checking ethcatherders organization membership...');
if (await checkDirectMembership('ethcatherders', username)) {
console.log(`✅ ${username} is confirmed as a member of ethcatherders org via direct API check`);
is_member = true;
} else {
const catMembers = await getAllOrgMembers('ethcatherders');
console.log(`Collected ${catMembers.length} ethcatherders members to check against`);
if (catMembers.includes(username.toLowerCase())) {
console.log(`✅ ${username} is a member of ethcatherders org`);
is_member = true;
} else {
console.log(`❌ ${username} is not found in ethcatherders org member list`);
}
}
}
// Fallback to a list of trusted contributors if not in either org
if (!is_member) {
if (trustedContributors.includes(username.toLowerCase())) {
console.log(`✅ ${username} is in the trusted contributors list`);
is_member = true;
} else {
console.log(`❌ ${username} is not in the trusted contributors list`);
}
}
console.log(`Final membership determination for ${username}: ${is_member ? 'Approved ✅' : 'Not approved ❌'}`);
} catch (error) {
console.log(`Error in membership check: ${error.message}`);
console.log(`Stack trace: ${error.stack}`);
// If we're here, something went wrong with the API checks
// Check if user is in trusted list as a fallback
if (trustedContributors.includes(username.toLowerCase())) {
console.log(`✅ FALLBACK: ${username} is in the trusted contributors list`);
is_member = true;
}
}
return is_member;
- name: Set output
id: set_output
run: echo "is_member=${{ steps.check_org_membership.outputs.result }}" >> $GITHUB_OUTPUT
handle_protocol_call:
needs: check_membership
runs-on: ubuntu-latest
if: needs.check_membership.outputs.is_member == 'true'
steps:
- name: Check out code
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Upgrade pip, setuptools, and wheel
run: |
pip install --upgrade pip setuptools wheel
- name: Install PyGithub Explicitly
run: |
pip install PyGithub>=1.55.1
- name: Install dependencies
run: |
pip install --upgrade pip
pip install -e .github/ACDbot/
pip install pytz google-api-python-client
pip install -r .github/ACDbot/requirements.txt
- name: Verify Installed Packages
run: |
pip list
- name: Log protocol call processing
run: |
echo "Processing protocol call issue #${{ github.event.issue.number }}"
echo "Issue title: ${{ github.event.issue.title }}"
echo "Issue labels: ${{ github.event.issue.labels.*.name }}"
- name: Handle protocol call
run: |
python .github/ACDbot/scripts/handle_protocol_call.py \
--issue_number "${{ github.event.issue.number }}" \
--repo "${{ github.repository }}"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ZOOM_CLIENT_ID: ${{ secrets.ZOOM_CLIENT_ID }}
ZOOM_CLIENT_SECRET: ${{ secrets.ZOOM_CLIENT_SECRET }}
ZOOM_ACCOUNT_ID: ${{ secrets.ZOOM_ACCOUNT_ID }}
ZOOM_ALTERNATIVE_HOSTS: ${{ secrets.ZOOM_ALTERNATIVE_HOSTS }}
ZOOM_REFRESH_TOKEN: ${{ secrets.ZOOM_REFRESH_TOKEN }}
DISCOURSE_API_KEY: ${{ secrets.DISCOURSE_API_KEY }}
DISCOURSE_API_USERNAME: ${{ secrets.DISCOURSE_API_USERNAME }}
DISCOURSE_BASE_URL: ${{ vars.DISCOURSE_BASE_URL }}
GCAL_ID: ${{ vars.GCAL_ID }}
GCAL_SERVICE_ACCOUNT_KEY: ${{ secrets.GCAL_SERVICE_ACCOUNT_KEY }}
GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }}
GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }}
YOUTUBE_REFRESH_TOKEN: ${{ secrets.YOUTUBE_REFRESH_TOKEN }}
YOUTUBE_API_KEY: ${{ secrets.YOUTUBE_API_KEY }}
GOOGLE_APPLICATION_CREDENTIALS: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }}
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
TELEGRAM_CHAT_ID: ${{ vars.TELEGRAM_CHAT_ID }}
SENDER_EMAIL: ${{ secrets.SENDER_EMAIL }}
SENDER_EMAIL_PASSWORD: ${{ secrets.SENDER_EMAIL_PASSWORD }}
SMTP_SERVER: ${{ secrets.SMTP_SERVER }}
- name: Commit mapping file
if: always()
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git config --local user.email "[email protected]"
git config --local user.name "GitHub Action"
if git diff --quiet .github/ACDbot/meeting_topic_mapping.json; then
echo "No changes to mapping file"
else
git add .github/ACDbot/meeting_topic_mapping.json
git commit -m "Update mapping for issue ${{ github.event.issue.number }}"
git push
echo "Committed mapping file changes"
fi
permissions:
contents: write
issues: write
# Prevent concurrent runs of the same issue
concurrency:
group: issue-${{ github.event.issue.number }}
cancel-in-progress: false