⭐ If you like this project, star it on GitHub — it helps a lot!
Overview • Getting started • Scripts • Shared Library • GitHub Actions • Resources
A collection of standalone bash scripts for GitHub organization administration. Automate common tasks like bulk permission management, repository creation, migration, and reporting using simple, self-contained utilities powered by the GitHub REST API.
This toolkit provides ready-to-use automation scripts for GitHub organization administrators. Each script is a complete, independent utility that can be run directly or integrated into your GitHub Actions workflows.
What you can do:
- Grant team permissions across all repositories in bulk
- Create repositories from templates with pre-configured access
- Mirror repositories with full git history
- Generate monthly issue reports with contributor statistics
- Track license consumption for enterprise accounts
- Archive old repositories
- Discover Dockerfiles and base images across an enterprise
- Organize your starred repositories
- And much more!
Architecture: Scripts use a shared utility library (lib/github-common.sh) for common validation, error handling, and API helpers. Script directories are grouped by domain (org-admin/, enterprise/, reporting/, personal/) and each script remains a self-contained .sh utility.
Built with simplicity: All scripts use only curl for API requests and jq for JSON processing—no complex dependencies or installation required beyond standard Unix tools.
Note
Scripts follow a convention-over-configuration approach with built-in validation and error handling. Each can be run standalone or integrated into CI/CD pipelines and GitHub Actions workflows.
- bash 4+
- curl - HTTP client for API requests
- jq - Command-line JSON processor
- git - For repository operations (required by some scripts)
- gh - GitHub CLI (required by
github-organize-stars) - GitHub Personal Access Token with appropriate scopes
Clone the repository:
git clone https://github.com/locus313/github-api-scripts.git
cd github-api-scriptsAlternatively, download individual scripts as needed—each script is standalone and can be used independently.
Create a GitHub Personal Access Token at github.com/settings/tokens with these scopes:
repo- Full control of repositoriesadmin:org- Organization administrationread:enterprise- Read enterprise data (for license scripts)
Export your token as an environment variable:
export GITHUB_TOKEN="ghp_your_token_here"Tip
Add this export to your ~/.bashrc or ~/.zshrc to persist the token across terminal sessions.
Scripts use environment variables for configuration. Common variables include:
| Variable | Description | Required |
|---|---|---|
GITHUB_TOKEN |
GitHub personal access token | Yes |
ORG |
Organization name | Yes (most scripts) |
API_URL_PREFIX |
GitHub API base URL (default: https://api.github.com) |
No |
GIT_URL_PREFIX |
GitHub base URL (default: https://github.com). Must be a GitHub-family host (github.com, *.github.com, *.ghe.com, *.githubenterprise.com). |
No |
Note
The *_PREFIX variables support GitHub Enterprise Server. Set them to your enterprise domain to use these scripts with GHES.
The repository ships versioned pre-commit hooks in .githooks/. Run the installer once after cloning:
./install-hooks.shThis sets core.hooksPath to .githooks/ so Git picks up the hooks automatically on every commit.
What the hook checks:
| Check | Tool | Fallback |
|---|---|---|
| Secret scanning | gitleaks |
Built-in regex patterns for GitHub tokens, AWS keys, private keys, and generic secrets |
| Shell script security | shellcheck |
Skipped with a warning if not installed |
Install the recommended tools for full coverage:
brew install gitleaks shellcheckTip
To bypass the hooks in an emergency: git commit --no-verify. Use sparingly — the hooks exist to prevent secrets from reaching the remote.
The repository includes a bats unit-test suite in tests/. Install bats and run the full suite:
# macOS
brew install bats-core
# Ubuntu / Debian
sudo apt-get install -y bats
# Run all tests
bats tests/| File | What it covers |
|---|---|
tests/test_common.bats |
lib/github-common.sh functions (validate_slug, require_env_var, gh_api sentinels, gh_api_paginate, etc.) |
tests/test_script_validation.bats |
Every script — missing required env vars, invalid CLI args, --help, and script-specific guards |
CI runs bats tests/ on every pull request alongside shellcheck.
Each script is a self-contained utility designed for a specific task. Navigate to the script's directory, set the required environment variables, and execute.
org-admin/:github-add-repo-collaborators-by-pattern,github-add-repo-permissions,github-archive-old-repos,github-auto-repo-creation,github-close-archived-repo-security-alerts,github-enable-issues,github-get-repo-list,github-import-repo,github-migrate-internal-repos-to-private,github-repo-from-templateenterprise/:github-add-enterprise-team-read-permissions,github-dockerfile-discovery,github-get-consumed-licenses,github-get-public-repos,github-increase-copilot-user-budget,github-install-enterprise-appreporting/:github-monthly-issues-report,github-repo-permissions-report,github-copilot-report,github-copilot-budget-advisor,github-enterprise-billing-reportpersonal/:github-organize-stars
Script: org-admin/github-add-repo-permissions/github-add-repo-permissions.sh
Grants team permissions across all repositories in an organization. Supports multiple permission levels (admin, maintain, push, triage, pull) and multiple teams per permission level.
Required variables:
export GITHUB_TOKEN="your_token"
export ORG="your-org"
# Set one or more permission levels (space-separated team slugs)
export REPO_ADMIN="platform-team ops-team" # Admin permissions
export REPO_MAINTAIN="maintainers" # Maintain permissions
export REPO_PUSH="developers contributors" # Write/push permissions
export REPO_TRIAGE="support-team" # Triage permissions
export REPO_PULL="external-auditors" # Read/pull permissions
# Optional: restrict to repos whose names start with a given prefix
export REPO_NAME_FILTER="my-service-"
# Optional: skip specific repos for a given permission level (space-separated repo names)
export REPO_PULL_EXCLUDE="secret-repo internal-tools" # These repos won't get pull access
# Optional: skip maintain/push/triage/pull on archived repos (admin is still applied)
export SKIP_ARCHIVED="true"Usage:
cd org-admin/github-add-repo-permissions
./github-add-repo-permissions.shWhat it does:
- Retrieves all repositories in the organization (paginated)
- Filters to repositories whose names start with
REPO_NAME_FILTER(all repos when unset) - Grants permissions to specified teams based on permission level
- Supports multiple teams per permission level (space-separated)
- Skips repos listed in the matching
REPO_<LEVEL>_EXCLUDEvariable for that permission level only - Optionally skips maintain/push/triage/pull permissions on archived repos when
SKIP_ARCHIVED=true(admin permissions are still applied on archived repos) - Processes all five GitHub permission levels: admin, maintain, push, triage, pull
- Includes 5-second delays between repos to avoid rate limits
Permission levels:
admin- Full repository access including settings and team managementmaintain- Repository management without admin privilegespush- Read and write access to codetriage- Read access plus ability to manage issues and pull requestspull- Read-only access to code
Note
At least one permission level must be set. Team slugs should be lowercase and hyphenated (e.g., "Platform Team" → platform-team).
Each permission level has an optional exclusion list: REPO_ADMIN_EXCLUDE, REPO_MAINTAIN_EXCLUDE, REPO_PUSH_EXCLUDE, REPO_TRIAGE_EXCLUDE, and REPO_PULL_EXCLUDE. Each holds space-separated exact repo names that are skipped for that level only.
Script: org-admin/github-repo-from-template/github-repo-from-template.sh
Creates a new repository from a template with pre-configured team permissions and collaborator access.
Required variables:
export GITHUB_TOKEN="your_token"
export ORG="your-org"
export TEMPLATE_REPO="template-repo"
export REPO_ADMIN="admins platform-team" # Space-separated team slugs
export REPO_WRITE="developers contributors" # Space-separated team slugs
export CD_USERNAME="ci-bot"
export CD_GITHUB_TOKEN="bot_token"Usage:
cd org-admin/github-repo-from-template
./github-repo-from-template.sh new-project-nameWhat it does:
- Creates a private repository from the specified template
- Includes all branches from the template
- Assigns admin permissions to teams in
REPO_ADMIN - Assigns write permissions to teams in
REPO_WRITE - Invites CD user as collaborator and auto-accepts the invitation
Note
REPO_NAME (CLI argument), TEMPLATE_REPO, CD_USERNAME, and all team slugs in REPO_ADMIN and REPO_WRITE are validated as slugs before use. Only alphanumeric characters, hyphens, and underscores are accepted.
Script: org-admin/github-import-repo/github-import-repo.sh
Performs a full repository mirror—clones source repo and pushes all branches, tags, and history to a new destination repo.
Required variables:
export GITHUB_TOKEN="your_token"
export ORG="your-org"
export OWNER_USERNAME="admin-user"Usage:
cd org-admin/github-import-repo
./github-import-repo.sh source-repo destination-repoWhat it does:
- Creates a new internal repository
- Performs bare clone of source repository
- Mirrors all git objects to destination
- Grants admin permissions to specified owner
- Cleans up local temporary clone
Warning
This creates a complete copy with full git history. Ensure you have sufficient disk space and network bandwidth for large repositories.
Note
GIT_URL_PREFIX is validated against a GitHub-host allowlist before any git operations run. Non-GitHub URLs are rejected to prevent credential leakage. Repository names and OWNER_USERNAME must be valid slugs (alphanumeric, hyphen, and underscore only).
Script: org-admin/github-add-repo-collaborators-by-pattern/github-add-repo-collaborators-by-pattern.sh
Adds one or more individual collaborators to all repositories in an organisation whose names match a given regex pattern.
Required variables:
export GITHUB_TOKEN="your_token"
export ORG="your-org"
export COLLABORATORS="alice,bob"
export REPO_NAME_REGEX='^service-'Usage:
cd org-admin/github-add-repo-collaborators-by-pattern
./github-add-repo-collaborators-by-pattern.sh| Variable | Description | Default |
|---|---|---|
PERMISSION |
Permission level: pull|triage|push|maintain|admin |
push |
REPO_EXCLUDE_REGEX |
ERE regex to exclude matching repository names | — |
What it does:
- Iterates all repositories in the organization (paginated)
- Filters to repos matching
REPO_NAME_REGEX - Optionally excludes repos matching
REPO_EXCLUDE_REGEX - Adds each specified collaborator with the configured permission level
Script: org-admin/github-archive-old-repos/github-archive-old-repos.sh
Identifies and archives repositories that have not been updated within a configurable number of years. Generates a timestamped CSV report.
Required variables:
export GITHUB_TOKEN="your_token"
export ORG="your-org"Usage:
cd org-admin/github-archive-old-repos
./github-archive-old-repos.shWhat it does:
- Fetches all repositories in the organization (paginated)
- Calculates a cutoff date based on
YEARS_THRESHOLD - Generates a timestamped CSV report in the
reports/subdirectory - Displays a summary with the top 10 oldest repositories
- Prompts for confirmation before archiving
- Archives qualifying repositories via the GitHub API, skipping already-archived ones
| Variable | Description | Default |
|---|---|---|
YEARS_THRESHOLD |
Age threshold in years | 5 |
REPORT_DIR |
Output directory for the CSV report | ./reports |
Script: org-admin/github-auto-repo-creation/github-auto-repo-creation.sh
Creates one or more private GitHub repositories in an organisation with standard configuration: branch protection on the default branch, a CODEOWNERS file, and optional team permissions.
Required variables:
export GITHUB_TOKEN="your_token"
export ORG="your-org"
export REPO_NAMES="repo1,repo2"
export REPO_OWNERS="platform-team"Usage:
cd org-admin/github-auto-repo-creation
./github-auto-repo-creation.shWhat it does:
- Creates each repository listed in
REPO_NAMESas private - Configures branch protection on the default branch
- Creates a CODEOWNERS file referencing the
REPO_OWNERSteams - Grants admin access to teams listed in
ADMIN_TEAMS
| Variable | Description | Default |
|---|---|---|
ADMIN_TEAMS |
Comma-separated team slugs for admin access | — |
Note
Requires base64 in addition to curl and jq (used to encode the CODEOWNERS file content for the API).
Note
All values in REPO_NAMES, ADMIN_TEAMS, and REPO_OWNERS are validated as slugs — only alphanumeric characters, hyphens, and underscores are allowed. The script exits with an error if any value fails this check.
Script: org-admin/github-close-archived-repo-security-alerts/github-close-archived-repo-security-alerts.sh
Dismisses or resolves all open security alerts (Dependabot, code scanning, and secret scanning) across all repositories in a GitHub organisation. Generates a CSV report of dismissed alerts.
Required variables:
export GITHUB_TOKEN="your_token"
export ORG="your-org"Usage:
cd org-admin/github-close-archived-repo-security-alerts
# Preview which alerts would be dismissed (no changes)
./github-close-archived-repo-security-alerts.sh --dry-run
# Dismiss only Dependabot alerts
./github-close-archived-repo-security-alerts.sh --type dependabot
# Dismiss all alert types
./github-close-archived-repo-security-alerts.shOptions:
| Flag | Description | Default |
|---|---|---|
--type <type> |
Alert type: dependabot | code-scanning | secret-scanning | all |
all |
--dry-run |
List alerts without dismissing them | — |
| Variable | Description | Default |
|---|---|---|
DEPENDABOT_REASON |
Dismiss reason for Dependabot alerts | tolerable_risk |
CODE_SCANNING_REASON |
Dismiss reason for code scanning alerts | won't fix |
SECRET_SCANNING_RESOLUTION |
Resolution for secret scanning alerts | wont_fix |
REPORT_DIR |
Output directory for the CSV report | ./reports |
What it does:
- Enumerates all repositories in the organization
- For each configured alert type, pages through all open alerts
- Dismisses or resolves alerts with the configured reason
- Generates a timestamped CSV report in the
reports/subdirectory
Important
Requires a token with security_events and repo scope. Use --dry-run first to preview the scope of changes.
Script: org-admin/github-enable-issues/github-enable-issues.sh
Enables the Issues feature on every repository in a GitHub organisation that currently has it disabled. Skips archived repositories.
Required variables:
export GITHUB_TOKEN="your_token"
export ORG="your-org"Usage:
cd org-admin/github-enable-issues
./github-enable-issues.sh --dry-run # Preview only
./github-enable-issues.sh # Apply changes| Flag | Description |
|---|---|
--dry-run |
List repositories that would be updated without making changes |
What it does:
- Iterates all non-archived repositories in the organization (paginated)
- Identifies repositories with Issues disabled
- Enables Issues on each matching repository via the PATCH API
Script: org-admin/github-get-repo-list/github-get-repo-list.sh
Lists all repositories in a GitHub organisation and outputs their metadata to stdout in CSV format.
Required variables:
export GITHUB_TOKEN="your_token"
export ORG="your-org"Usage:
cd org-admin/github-get-repo-list
./github-get-repo-list.sh
./github-get-repo-list.sh > repos.csvWhat it does:
- Fetches all repositories (paginated, 100 per page)
- Outputs a CSV row per repository with: full name, owner, visibility, URL, description, fork flag, pushed/created/updated timestamps