This repository runs an organization-wide GitHub governance scan on a schedule using R and GitHub Actions.
The automation performs two checks across repositories in a GitHub organization:
- For repositories without
.github/CODEOWNERS, it inspects recent commit history and creates a CODEOWNERS file for the top recent contributor. - For repositories with
.github/CODEOWNERS, it checks for inactivity over the configured lookback window and opens an issue tagging the CODEOWNERS when the repository appears inactive. Individual CODEOWNERS users are also assigned to the issue when GitHub allows assignment for that repository.
.github/workflows/governance.yml
R/
assign_codeowners.R
check_inactive_repos.R
github_utils.R
main.R
config/
config.yaml
README.md
Update config/config.yaml before running:
org: "psrc"
lookback_months: 24
excluded_users:
- "dependabot[bot]"
- "github-actions[bot]"
min_contribution_threshold: 0.3
require_org_membership: true
ignored_activity_paths:
- ".github/CODEOWNERS"
ignored_activity_title_patterns:
- "(?i)codeowners"min_contribution_threshold is used as follows:
- If a single contributor owns at least that share of recent commits, the automation assigns that user alone.
- If no single contributor is dominant, the automation assigns multiple top contributors until the cumulative contribution share reaches the threshold, with a minimum of two owners.
Repositories that are archived are skipped. Forks are also skipped by default.
For inactivity checks, the default configuration ignores governance-only activity tied to .github/CODEOWNERS rollouts. That keeps a bulk CODEOWNERS backfill from making dormant repositories look active. You can widen or narrow the filters with ignored_activity_paths and ignored_activity_title_patterns.
Set a GH_TOKEN secret at the repository or organization level.
The script passes GH_TOKEN explicitly to the R gh client.
Classic PAT requirements:
repofor private repository contents, commits, pull requests, issues, and assignee checksread:orgto read organization members
Fine-grained PAT requirements:
- organization permission
Members: Read-only - repository permission
Contents: Read and write - repository permission
Pull requests: Read-only - repository permission
Issues: Read and write
For a fine-grained PAT, grant access to every repository the workflow should scan.
Known caveat with fine-grained PATs:
- This script uses
/orgs/{org}/membersas the authoritative source for organization membership because/user/memberships/orgsand/user/memberships/orgs/{org}can be empty or inconsistent with fine-grained PATs.
The workflow is defined in .github/workflows/governance.yml.
It runs:
- on a schedule at 06:00 UTC on March 1 and September 1
- manually through
workflow_dispatch
The workflow installs R, installs the required packages, and runs:
Rscript R/main.RInstall the required packages locally:
install.packages(c("gh", "dplyr", "purrr", "yaml", "stringr"), repos = "https://cloud.r-project.org")Export a token and run the script from the repository root:
export GH_TOKEN=your_token_here
Rscript R/main.R --config config/config.yaml --dry-runOn Windows PowerShell:
$env:GH_TOKEN = "your_token_here"
Rscript R/main.R --config config/config.yaml --dry-run--dry-run logs intended writes without creating CODEOWNERS files or issues.
After pushing this repository to GitHub:
- Open the repository Actions tab.
- Select the
Organization Governanceworkflow. - Choose
Run workflow.
- Repositories with no commits in the lookback window are skipped.
- Commits without a mapped GitHub author are ignored.
- When
require_org_membershipistrue, contributors who are not organization members are excluded from CODEOWNERS selection. - If all contributors are excluded by membership filtering, the repository is skipped and a warning is logged.
- Inactivity checks ignore CODEOWNERS-only commits and merged PRs by default.
- Inactivity issues mention all CODEOWNERS entries and assign any individual CODEOWNERS users who are valid assignees on that repository. Team CODEOWNERS are mentioned but not assigned.
- Duplicate inactivity issues are avoided by checking for an existing open issue with the same title.