This script analyzes user contribution activity across GitHub organizations by tracking commits, issues, and pull requests across all repositories and branches.
Based on these contributions, it determines each userβs last recorded activity and classifies users as Active or Inactive using a configurable inactivity threshold.
The script generates detailed, per-repository CSV reports for auditing and analysis.
This project is available both as:
- β A GitHub Action
- π A standalone Python script (optional local usage)
This project is available as a GitHub Action, allowing you to generate detailed user activity reports automatically without local setup.
- Reads organization names, inactivity threshold, and authentication details from GitHub Action inputs, which are passed to the script as environment variables internally.
- Retrieves all members of each configured GitHub organization.
- Fetches all non-fork repositories within the organization.
- Enumerates all branches for each repository.
- Collects user activity across:
- Commits (per branch)
- Issues (created)
- Pull requests (created)
- Aggregates activity per user to determine:
- Total commits, issues, and PRs
- Most recent activity timestamp
- Classifies users as:
- Active β last activity within the configured inactivity threshold
- Inactive β User has no recorded commits, issues, or pull requests, or their most recent activity is older than the configured threshold.
- Generates a consolidated CSV report with per-repository user activity details.
- Implements retry logic, timeouts, and rate-limit delays to ensure reliable execution against the GitHub GraphQL API.
- name: Run user contribution activity report
uses: CanarysAutomations/User-Contribution-Activity@v1.0.0
with:
github_token: ${{ secrets.ORG_AUDIT_TOKEN }}
org_names: org-name
days_inactive_threshold: 90
# Uploads the CSV report of user contribution activity
- name: Upload contribution activity report
uses: actions/upload-artifact@v4
with:
name: users-contribution-activity-report
path: "*.csv"
β οΈ Important:
The GitHub Marketplace installation snippet does not include secret values.
You must create a GitHub secret (for example,ORG_AUDIT_TOKEN) and explicitly pass it to the action as shown below:github_token: ${{ secrets.ORG_AUDIT_TOKEN }}
name: user contribution activity audit
on:
workflow_dispatch:
schedule:
- cron: "0 2 1 * *"
jobs:
audit:
runs-on: ubuntu-latest
steps:
- name: Run user contribution activity report
uses: CanarysAutomations/User-Contribution-Activity@v1.0.0
with:
github_token: ${{ secrets.ORG_AUDIT_TOKEN }}
org_names: org-name
days_inactive_threshold: 90
- name: Upload contribution activity report
uses: actions/upload-artifact@v4
with:
name: users-contribution-activity-report
path: "*.csv"
The token used with this action must have the following scopes:
-
read:org β required to list organization members -
repo β required only if the organization contains private repositories
| Name | Required | Default | Description |
|---|---|---|---|
github_token |
Yes | β | GitHub token with read:org and repository read access |
org_names |
Yes | β | Comma-separated GitHub organization names |
days_inactive_threshold |
No | 60 | Days to consider a user inactive |
Note:
days_inactive_threshold specifies the number of days without contribution activity (commits, issues, or pull requests) after which a user is marked as Inactive. If not provided, the default value of 60 days is used.
| Username | Commits | Issues | PRs | Last Activity | Status |
|---|---|---|---|---|---|
| Sandeep-U-D | 4 | 1 | 0 | 2025-07-15T04:01:00Z | Active |
| nikhilgowda-135 | 1 | 2 | 0 | 2025-07-29T06:34:53Z | Active |
| niranjanakoni | 0 | 0 | 0 | N/A | Inactive |
| praveensh-git | 0 | 0 | 0 | N/A | Inactive |
| raghav-s23 | 0 | 0 | 0 | N/A | Inactive |
The script uses the GitHub GraphQL API to:
- Retrieve all members from one or more GitHub organizations
- Fetch all repositories (excluding forks) from each organization
- Analyze all branches in each repository
- Track commits, issues, and pull requests for each user
- Determine user activity status based on a configurable inactivity threshold
- Generate separate timestamped CSV reports for each organization
- Python 3.x
- Required Python packages:
requestspython-dotenv
- Install the required packages:
pip install requests python-dotenv- Create a
.envfile in the same directory with the following variables:
GITHUB_TOKEN=your-github-personal-access-token
ORG_NAMES=org1,org2,org3
DAYS_INACTIVE_THRESHOLD=60
- GITHUB_TOKEN: Your GitHub Personal Access Token
- Required scopes:
repo,read:org - Note: This script uses GraphQL API, so ensure the token has appropriate permissions
- Required scopes:
- ORG_NAMES: Comma-separated list of GitHub organization names to analyze
- Example:
myorg1,myorg2,myorg3
- Example:
- DAYS_INACTIVE_THRESHOLD: Number of days to consider a user inactive (default: 60)
- Users with no activity in this period will be marked as "Inactive"
Run the script:
python user_contribution_activity.pyThe script generates a timestamped CSV file for each organization (e.g., myorg_user_activity_20250124_143022.csv).
The output is organized by repository with the following structure:
Repository: repo-name
Username, Commits, Issues, PRs, Last Activity, Status
user1, 45, 12, 8, 2025-01-15T10:30:00Z, Active
user2, 0, 0, 0, N/A, Inactive
...
Repository: another-repo
Username, Commits, Issues, PRs, Last Activity, Status
...
- Username: GitHub username of the organization member
- Commits: Total number of commits by the user in the repository (across all branches)
- Issues: Total number of issues created by the user
- PRs: Total number of pull requests created by the user
- Last Activity: Most recent activity timestamp (latest of commit/issue/PR)
- Status: "Active" or "Inactive" based on the configured threshold
- β Supports multiple organizations in a single run
- β Analyzes all branches in each repository
- β Tracks three types of activity: commits, issues, and pull requests
- β Automatic pagination handling for large datasets
- β Uses efficient GraphQL API for faster data retrieval
- β Configurable inactivity threshold
- β Timestamped output files
- β Shows all organization members, even those with no activity
- β Real-time progress indicators
- β UTF-8 encoding support
- β Built-in rate limiting delays (2 seconds between requests)
- Active: User has activity within the configured threshold period
- Example: If threshold is 60 days, any activity in the last 60 days marks the user as Active
- Inactive: User has no activity within the threshold period, or no activity at all
- This script can take significant time to complete for large organizations
- Time estimate: ~1-5 minutes per repository, depending on size
- The script processes all branches in each repository
- GraphQL API is used for efficiency, but rate limits still apply
- Built-in 2-second delay between requests to prevent rate limiting
- 5,000 points per hour for authenticated requests
- Different queries consume different point values
- The script includes delays to stay within limits
- Complex queries (commit history) consume more points
The script handles:
- GraphQL query failures with detailed error messages
- Missing or null data gracefully
- Empty repositories or branches
- Users with no activity (shows as "N/A")
- Timezone conversions for accurate date comparisons
This script is useful for:
- Identifying dormant or inactive organization members
- Auditing user contributions across repositories
- Planning user access reviews
- Understanding team activity patterns
- Compliance and security audits
- Identifying users who may no longer need access
To analyze the results:
- Open the generated CSV file in Excel or a text editor
- Filter by "Status" column to find inactive users
- Sort by "Last Activity" to see most recent contributors
- Review activity counts to understand contribution levels
- Use data for access reviews or team planning
- All organization members are included in the output, regardless of activity level
- Forks are excluded from repository analysis
- The script fetches complete activity history (not limited by time)
- Commit activity is aggregated across all branches
- Debug output shows issue processing in real-time
- Separate CSV files are generated for each organization
Script runs slowly:
- This is normal for large organizations with many repositories
- Consider running during off-peak hours
- The script must process all branches and all history
"GraphQL query failed" error:
- Check that your token has the required scopes
- Verify the token hasn't expired
- Ensure organization names are spelled correctly
Users missing from report:
- Verify they are organization members (not outside collaborators)
- Check that they have a GitHub account (not deleted)
- Read access to organization membership
- Read access to all repositories
- Token with
repoandread:orgscopes