GNOME Nepal - Top contributors calculator

This system automatically aggregates contributor data from all organization repositories using the GitHub API, updates a sorted JSON file every hour, and triggers a webhook whenever changes occur. It ensures contributor information remains accurate, version-controlled, and easily integrated into GNOME Website and Discord


One of the repetitive tasks in any open-source organization is keeping contributor information up to date. Contributors change, new people join, old ones go quiet — and keeping a static list is both tedious and error-prone.
To solve this, I built a small automation pipeline around GitHub Actions and the GitHub API. It does two things:
  1. Poll all repositories under the GNOME-Nepal organization every hour, update the contributor list, and commit changes to the repo.
  2. Broadcast changes through a webhook whenever contributors.json is updated.
This post breaks down how the algorithm works and how the two workflows tie together.

The Python Script: Building the Contributor Index

The heart of the system is a Python script that queries GitHub’s REST API and aggregates contributor data across all repositories in the organization.

Step 1: Fetch All Repositories

repos_url = f"{BASE_URL}/orgs/{ORG_NAME}/repos"
repos_response = requests.get(repos_url, headers=headers)
This hits the /orgs/:org/repos endpoint to list every repo in the organization.

Step 2: Collect Contributor Counts

For each repository, the script calls the /repos/:org/:repo/contributors endpoint, which returns contributors and their contribution counts.
The script filters out non-user accounts (like bots or the actions-user) and accumulates contributions into a dictionary keyed by username:
if username not in contributor_contribution_dict:
    contributor_contribution_dict[username] = 0
contributor_contribution_dict[username] += repo_contributor["contributions"]

Step 3: Enrich Contributor Profiles

Once raw counts are collected, the script calls /users/:username to fetch profile details for each contributor. This way, the final dataset includes not only contribution numbers but also user metadata such as profile URLs, avatars, etc.
user_information_url = f"{BASE_URL}/users/{username}"
response = requests.get(user_information_url, headers=headers)

Step 4: Sort and Persist

The final list is sorted by contributions (highest first) and dumped into contributors.json:
with open("contributors.json", "w") as c:
    json.dump(full_information, c, indent=2, ensure_ascii=False)
At this point, the JSON file represents a live, ranked leaderboard of contributors across the entire org.
The workflow
The workflow

Workflow 1: Hourly Data Refresh

The first GitHub Actions workflow (Run Python Script and Commit Changes) ensures contributors.json is always current.
  • It runs every hour via a cron trigger.
  • Checks out the repository.
  • Sets up Python and installs dependencies.
  • Executes script.py with a GitHub token (MAINTAINERS_POLLING_TOKEN).
  • Commits and pushes the updated contributors.json back to main with a timestamped commit message.
This effectively turns GitHub Actions into a scheduled ETL job — extract from GitHub’s API, transform into a sorted list, load into version control.

Workflow 2: Broadcasting Updates

The second workflow (Update Contributors Webhook) activates whenever contributors.json changes.
  • Reads the updated JSON with jq.
  • Iterates through each contributor, extracting their username and profile URL.
  • Constructs an embed payload.
  • Sends it via a PATCH request to a configured webhook URL.
This is how updates get pushed outward — whether to a Discord channel, Slack, or another external system.

Why This Works Well

  • Automated polling ensures data freshness without manual intervention.
  • Version-controlled JSON gives a full history of contributor changes over time.
  • Event-driven webhooks allow the data to be propagated wherever needed.
  • Simple and transparent — the whole system is just Python + YAML + GitHub Actions.

Closing Thoughts

This setup gives me an always-up-to-date snapshot of organizational contributors, enriched with profile data, and automatically propagated to wherever it needs to be consumed. It’s not complex infrastructure — just a few hundred lines of Python and two YAML workflows — but it reliably handles what used to be a manual and messy task.
The result: a continuously refreshed, versioned, and broadcast contributor index for the entire org.

Want more? Check reading lists too

Stay up-to-date with my latest projects, articles, and insights.