Skip to content

README Maintainer

README Maintainer #353

name: README Maintainer
on:
pull_request_target:
types: [closed]
push:
branches: [main]
schedule:
- cron: '0 0 * * *' # Run daily
workflow_dispatch: # Manual trigger
jobs:
update:
runs-on: ubuntu-latest
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' || (github.event_name == 'pull_request_target' && github.event.pull_request.merged == true)
permissions:
contents: write
pull-requests: read
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: main
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install PyGithub
- name: Update README
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
python - <<EOF
import re
import os
import glob
from github import Github
# Hardcoded contributors to always keep
PRESERVED_CONTRIBUTORS = {
'riteshdavv': ('Ritesh Kumar Singh', 'https://github.com/riteshdavv'),
'officialasishkumar': ('Asish Kumar', 'https://github.com/officialasishkumar'),
'JayKapadia389': ('Jay Kapadia', 'https://github.com/JayKapadia389')
}
def get_org_list_from_directories():
"""Get organization list from directories in the repository"""
org_dirs = []
for dir_path in glob.glob("*"):
if os.path.isdir(dir_path) and not dir_path.startswith('.'):
# Replace underscore with space and capitalize each word for display
org_name = dir_path
org_dirs.append(org_name)
return sorted(org_dirs)
def update_org_list(content):
"""Update the organization list based on directories in the repo"""
org_dirs = get_org_list_from_directories()
# Extract the org list section
org_section = re.search(r'(## Org Submission Checklist\s*\n+)((?:- \[.\][^\n]*\n+)*)', content, re.MULTILINE)
if not org_section:
return content
# Create updated checklist with all organizations checked
org_lines = []
for org in org_dirs:
org_lines.append(f"- [x] {org}")
# Replace section with updated list
new_section = org_section.group(1) + '\n'.join(org_lines) + '\n\n'
return content[:org_section.start()] + new_section + content[org_section.end():]
def count_proposals():
"""Count total number of PDF proposals in the repository"""
return len(glob.glob("**/*.pdf", recursive=True))
def update_proposal_count(content):
"""Update the proposal count in the README"""
count = count_proposals()
count_match = re.search(r'(Total proposals submitted: \*\*)\d+(\*\*)', content)
if count_match:
return content[:count_match.start(1)] + f"Total proposals submitted: **{count}**" + content[count_match.end(2):]
return content
def get_contributors():
g = Github(os.getenv('GITHUB_TOKEN'))
repo = g.get_repo(os.getenv('GITHUB_REPOSITORY'))
# Start with preserved contributors
contributors = {username: (name, url) for username, (name, url) in PRESERVED_CONTRIBUTORS.items()}
# Get all contributors from GitHub
try:
# Get all contributors, including those who haven't committed recently
for contributor in repo.get_contributors(anon=False):
username = contributor.login.lower()
if not any(bot in username for bot in ['[bot]', 'actions-user', 'dependabot']):
contributors[username] = (
contributor.name or contributor.login,
f'https://github.com/{contributor.login}'
)
except Exception as e:
print(f"Error getting contributors: {e}")
# Add PR author if this is a merged PR
if os.getenv('GITHUB_EVENT_NAME') == 'pull_request_target':
try:
with open(os.environ.get('GITHUB_EVENT_PATH')) as f:
import json
event = json.load(f)
if event.get('pull_request', {}).get('user'):
pr_user = event['pull_request']['user']
username = pr_user['login'].lower()
if not any(bot in username for bot in ['[bot]', 'actions-user', 'dependabot']):
contributors[username] = (
pr_user.get('name') or pr_user['login'],
f"https://github.com/{pr_user['login']}"
)
except Exception as e:
print(f"Error processing PR author from event file: {e}")
try:
# Extract PR number from the ref
ref_parts = os.environ.get('GITHUB_REF', '').split('/')
if len(ref_parts) >= 3 and ref_parts[1] == 'pull':
pr_number = int(ref_parts[2])
if pr_number:
pr = repo.get_pull(pr_number)
if pr.merged and not any(bot in pr.user.login.lower() for bot in ['[bot]', 'actions-user', 'dependabot']):
username = pr.user.login.lower()
contributors[username] = (
pr.user.name or pr.user.login,
f'https://github.com/{pr.user.login}'
)
except Exception as e:
print(f"Error processing PR author from ref: {e}")
# Also check for any recent commits that might have been missed
try:
commits = repo.get_commits()
for commit in commits[:50]: # Check last 50 commits
if commit.author and not any(bot in commit.author.login.lower() for bot in ['[bot]', 'actions-user', 'dependabot']):
username = commit.author.login.lower()
contributors[username] = (
commit.author.name or commit.author.login,
f'https://github.com/{commit.author.login}'
)
except Exception as e:
print(f"Error checking recent commits: {e}")
return contributors
def update_contributors_section(content, contributors):
# Find contributors section
contrib_match = re.search(r'(## Contributors\s*\n+<!-- Add contributors below -->)(.*?)(\n+##|\Z)', content, re.DOTALL)
if not contrib_match:
return content
# Build new contributors list
contrib_lines = []
for username, (name, url) in sorted(contributors.items(), key=lambda x: x[1][0].lower()):
contrib_lines.append(f"- [{name}]({url})")
# Build avatar section
avatar_html = "<div align=\"center\">\n"
for username, (_, url) in sorted(contributors.items()):
github_username = url.split('/')[-1]
avatar_html += f' <a href="{url}"><img src="https://github.com/{github_username}.png" width="60px" alt="{github_username}" /></a>\n'
avatar_html += "</div>\n\n"
# Replace section
new_section = (
contrib_match.group(1) + '\n' +
'\n'.join(contrib_lines) + '\n\n' +
avatar_html
)
return content[:contrib_match.start()] + new_section + content[contrib_match.end(0):]
# Read current README
with open('README.md', 'r', encoding='utf-8') as f:
content = f.read()
# Update proposal count
content = update_proposal_count(content)
# Update org list from directories
content = update_org_list(content)
# Update contributors
contributors = get_contributors()
content = update_contributors_section(content, contributors)
# Ensure consistent spacing before sections
content = re.sub(r'\n{3,}##', r'\n\n##', content)
# Write updated README
with open('README.md', 'w', encoding='utf-8') as f:
f.write(content)
EOF
- name: Commit changes
run: |
git config --local user.email "[email protected]"
git config --local user.name "GitHub Action"
git add README.md
git diff --quiet && git diff --staged --quiet || (
if [[ "${{ github.event_name }}" == "pull_request_target" ]]; then
git commit -m "Update README after PR merge - Add new org and contributors"
else
git commit -m "Update README organization list and contributors"
fi
# Pull latest changes before pushing
git pull --rebase origin main
git push
)