{"id":19138146,"url":"https://github.com/locus313/github-api-scripts","last_synced_at":"2026-06-27T07:01:59.012Z","repository":{"id":48443246,"uuid":"382846115","full_name":"locus313/github-api-scripts","owner":"locus313","description":"Bash automation scripts for GitHub organization and enterprise administration using the REST and GraphQL APIs","archived":false,"fork":false,"pushed_at":"2026-06-26T19:50:15.000Z","size":522,"stargazers_count":2,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-06-26T20:19:46.402Z","etag":null,"topics":["automation","bash","cli","devops","github","github-actions","github-api","github-enterprise","organization-management","shell-script"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/locus313.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null},"funding":{"github":"locus313"}},"created_at":"2021-07-04T12:26:25.000Z","updated_at":"2026-06-26T19:50:12.000Z","dependencies_parsed_at":"2025-02-22T18:42:06.660Z","dependency_job_id":"1a965226-4bb2-44d1-a4b6-6e279e7e2e5c","html_url":"https://github.com/locus313/github-api-scripts","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/locus313/github-api-scripts","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/locus313%2Fgithub-api-scripts","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/locus313%2Fgithub-api-scripts/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/locus313%2Fgithub-api-scripts/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/locus313%2Fgithub-api-scripts/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/locus313","download_url":"https://codeload.github.com/locus313/github-api-scripts/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/locus313%2Fgithub-api-scripts/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34844346,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-27T02:00:06.362Z","response_time":126,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["automation","bash","cli","devops","github","github-actions","github-api","github-enterprise","organization-management","shell-script"],"created_at":"2024-11-09T06:41:54.023Z","updated_at":"2026-06-27T07:01:59.005Z","avatar_url":"https://github.com/locus313.png","language":"Shell","funding_links":["https://github.com/sponsors/locus313"],"categories":[],"sub_categories":[],"readme":"# GitHub API Scripts\n\n[![AI Ready](https://img.shields.io/badge/AI--Ready-yes-brightgreen?style=flat)](https://github.com/johnpapa/ai-ready)\n[![License](https://img.shields.io/badge/License-MIT-yellow?style=flat-square)](LICENSE)\n![Shell](https://img.shields.io/badge/Shell-Bash-89e051?style=flat-square\u0026logo=gnu-bash\u0026logoColor=white)\n[![GitHub API](https://img.shields.io/badge/GitHub_API-v3-blue?style=flat-square\u0026logo=github)](https://docs.github.com/en/rest)\n\n⭐ If you like this project, star it on GitHub — it helps a lot!\n\n[Overview](#overview) • [Getting started](#getting-started) • [Scripts](#scripts) • [Shared Library](#shared-library-libgithub-commonsh) • [GitHub Actions](#using-scripts-in-github-actions) • [Resources](#resources)\n\nA 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.\n\n## Overview\n\nThis 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.\n\n**What you can do:**\n- Grant team permissions across all repositories in bulk\n- Create repositories from templates with pre-configured access\n- Mirror repositories with full git history\n- Generate monthly issue reports with contributor statistics\n- Track license consumption for enterprise accounts\n- Archive old repositories\n- Discover Dockerfiles and base images across an enterprise\n- Organize your starred repositories\n- And much more!\n\n**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.\n\n**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.\n\n\u003e [!NOTE]\n\u003e 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.\n\n## Getting started\n\n### Prerequisites\n\n- **bash** 4+\n- **[curl](https://curl.se)** - HTTP client for API requests\n- **[jq](https://stedolan.github.io/jq)** - Command-line JSON processor\n- **[git](https://git-scm.com)** - For repository operations (required by some scripts)\n- **[gh](https://cli.github.com)** - GitHub CLI (required by `github-organize-stars`)\n- **GitHub Personal Access Token** with appropriate scopes\n\n### Installation\n\nClone the repository:\n\n```bash\ngit clone https://github.com/locus313/github-api-scripts.git\ncd github-api-scripts\n```\n\nAlternatively, download individual scripts as needed—each script is standalone and can be used independently.\n\n### Authentication\n\nCreate a GitHub Personal Access Token at [github.com/settings/tokens](https://github.com/settings/tokens) with these scopes:\n\n- `repo` - Full control of repositories\n- `admin:org` - Organization administration\n- `read:enterprise` - Read enterprise data (for license scripts)\n\nExport your token as an environment variable:\n\n```bash\nexport GITHUB_TOKEN=\"ghp_your_token_here\"\n```\n\n\u003e [!TIP]\n\u003e Add this export to your `~/.bashrc` or `~/.zshrc` to persist the token across terminal sessions.\n\n### Configuration\n\nScripts use environment variables for configuration. Common variables include:\n\n| Variable | Description | Required |\n|----------|-------------|----------|\n| `GITHUB_TOKEN` | GitHub personal access token | Yes |\n| `ORG` | Organization name | Yes (most scripts) |\n| `API_URL_PREFIX` | GitHub API base URL (default: `https://api.github.com`) | No |\n| `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 |\n\n\u003e [!NOTE]\n\u003e The `*_PREFIX` variables support GitHub Enterprise Server. Set them to your enterprise domain to use these scripts with GHES.\n\n### Pre-commit Hooks\n\nThe repository ships versioned pre-commit hooks in `.githooks/`. Run the installer once after cloning:\n\n```bash\n./install-hooks.sh\n```\n\nThis sets `core.hooksPath` to `.githooks/` so Git picks up the hooks automatically on every commit.\n\n**What the hook checks:**\n\n| Check | Tool | Fallback |\n|-------|------|----------|\n| Secret scanning | [`gitleaks`](https://github.com/gitleaks/gitleaks) | Built-in regex patterns for GitHub tokens, AWS keys, private keys, and generic secrets |\n| Shell script security | [`shellcheck`](https://www.shellcheck.net) | Skipped with a warning if not installed |\n\nInstall the recommended tools for full coverage:\n\n```bash\nbrew install gitleaks shellcheck\n```\n\n\u003e [!TIP]\n\u003e To bypass the hooks in an emergency: `git commit --no-verify`. Use sparingly — the hooks exist to prevent secrets from reaching the remote.\n\n## Scripts\n\nEach script is a self-contained utility designed for a specific task. Navigate to the script's directory, set the required environment variables, and execute.\n\n### Folder Layout (Domain-Based)\n\n- `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-template`\n- `enterprise/`: `github-add-enterprise-team-read-permissions`, `github-dockerfile-discovery`, `github-get-consumed-licenses`, `github-get-public-repos`, `github-install-enterprise-app`\n- `reporting/`: `github-monthly-issues-report`, `github-repo-permissions-report`, `github-copilot-report`\n- `personal/`: `github-organize-stars`\n\n### Add Repository Permissions\n\n**Script:** `org-admin/github-add-repo-permissions/github-add-repo-permissions.sh`\n\nGrants team permissions across all repositories in an organization. Supports multiple permission levels (admin, maintain, push, triage, pull) and multiple teams per permission level.\n\n**Required variables:**\n```bash\nexport GITHUB_TOKEN=\"your_token\"\nexport ORG=\"your-org\"\n\n# Set one or more permission levels (space-separated team slugs)\nexport REPO_ADMIN=\"platform-team ops-team\"      # Admin permissions\nexport REPO_MAINTAIN=\"maintainers\"              # Maintain permissions\nexport REPO_PUSH=\"developers contributors\"      # Write/push permissions\nexport REPO_TRIAGE=\"support-team\"               # Triage permissions\nexport REPO_PULL=\"external-auditors\"            # Read/pull permissions\n\n# Optional: restrict to repos whose names start with a given prefix\nexport REPO_NAME_FILTER=\"my-service-\"\n```\n\n**Usage:**\n```bash\ncd org-admin/github-add-repo-permissions\n./github-add-repo-permissions.sh\n```\n\n**What it does:**\n- Retrieves all repositories in the organization (paginated)\n- Filters to repositories whose names start with `REPO_NAME_FILTER` (all repos when unset)\n- Grants permissions to specified teams based on permission level\n- Supports multiple teams per permission level (space-separated)\n- Processes all five GitHub permission levels: admin, maintain, push, triage, pull\n- Includes 5-second delays between repos to avoid rate limits\n\n**Permission levels:**\n- `admin` - Full repository access including settings and team management\n- `maintain` - Repository management without admin privileges\n- `push` - Read and write access to code\n- `triage` - Read access plus ability to manage issues and pull requests\n- `pull` - Read-only access to code\n\n\u003e [!NOTE]\n\u003e At least one permission level must be set. Team slugs should be lowercase and hyphenated (e.g., \"Platform Team\" → `platform-team`).\n\n---\n\n### Create Repository from Template\n\n**Script:** `org-admin/github-repo-from-template/github-repo-from-template.sh`\n\nCreates a new repository from a template with pre-configured team permissions and collaborator access.\n\n**Required variables:**\n```bash\nexport GITHUB_TOKEN=\"your_token\"\nexport ORG=\"your-org\"\nexport TEMPLATE_REPO=\"template-repo\"\nexport REPO_ADMIN=\"admins platform-team\"     # Space-separated team slugs\nexport REPO_WRITE=\"developers contributors\"  # Space-separated team slugs\nexport CD_USERNAME=\"ci-bot\"\nexport CD_GITHUB_TOKEN=\"bot_token\"\n```\n\n**Usage:**\n```bash\ncd org-admin/github-repo-from-template\n./github-repo-from-template.sh new-project-name\n```\n\n**What it does:**\n- Creates a private repository from the specified template\n- Includes all branches from the template\n- Assigns admin permissions to teams in `REPO_ADMIN`\n- Assigns write permissions to teams in `REPO_WRITE`\n- Invites CD user as collaborator and auto-accepts the invitation\n\n\u003e [!NOTE]\n\u003e `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.\n\n---\n\n### Import Repository\n\n**Script:** `org-admin/github-import-repo/github-import-repo.sh`\n\nPerforms a full repository mirror—clones source repo and pushes all branches, tags, and history to a new destination repo.\n\n**Required variables:**\n```bash\nexport GITHUB_TOKEN=\"your_token\"\nexport ORG=\"your-org\"\nexport OWNER_USERNAME=\"admin-user\"\n```\n\n**Usage:**\n```bash\ncd org-admin/github-import-repo\n./github-import-repo.sh source-repo destination-repo\n```\n\n**What it does:**\n- Creates a new internal repository\n- Performs bare clone of source repository\n- Mirrors all git objects to destination\n- Grants admin permissions to specified owner\n- Cleans up local temporary clone\n\n\u003e [!WARNING]\n\u003e This creates a complete copy with full git history. Ensure you have sufficient disk space and network bandwidth for large repositories.\n\n\u003e [!NOTE]\n\u003e `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).\n\n---\n\n### Add Repository Collaborators by Pattern\n\n**Script:** `org-admin/github-add-repo-collaborators-by-pattern/github-add-repo-collaborators-by-pattern.sh`\n\nAdds one or more individual collaborators to all repositories in an organisation whose names match a given regex pattern.\n\n**Required variables:**\n```bash\nexport GITHUB_TOKEN=\"your_token\"\nexport ORG=\"your-org\"\nexport COLLABORATORS=\"alice,bob\"\nexport REPO_NAME_REGEX='^service-'\n```\n\n**Usage:**\n```bash\ncd org-admin/github-add-repo-collaborators-by-pattern\n./github-add-repo-collaborators-by-pattern.sh\n```\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `PERMISSION` | Permission level: `pull\\|triage\\|push\\|maintain\\|admin` | `push` |\n| `REPO_EXCLUDE_REGEX` | ERE regex to exclude matching repository names | — |\n\n**What it does:**\n- Iterates all repositories in the organization (paginated)\n- Filters to repos matching `REPO_NAME_REGEX`\n- Optionally excludes repos matching `REPO_EXCLUDE_REGEX`\n- Adds each specified collaborator with the configured permission level\n\n---\n\n### Archive Old Repositories\n\n**Script:** `org-admin/github-archive-old-repos/github-archive-old-repos.sh`\n\nIdentifies and archives repositories that have not been updated within a configurable number of years. Generates a timestamped CSV report.\n\n**Required variables:**\n```bash\nexport GITHUB_TOKEN=\"your_token\"\nexport ORG=\"your-org\"\n```\n\n**Usage:**\n```bash\ncd org-admin/github-archive-old-repos\n./github-archive-old-repos.sh\n```\n\n**What it does:**\n- Fetches all repositories in the organization (paginated)\n- Calculates a cutoff date based on `YEARS_THRESHOLD`\n- Generates a timestamped CSV report in the `reports/` subdirectory\n- Displays a summary with the top 10 oldest repositories\n- Prompts for confirmation before archiving\n- Archives qualifying repositories via the GitHub API, skipping already-archived ones\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `YEARS_THRESHOLD` | Age threshold in years | `5` |\n\n---\n\n### Auto Repository Creation\n\n**Script:** `org-admin/github-auto-repo-creation/github-auto-repo-creation.sh`\n\nCreates 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.\n\n**Required variables:**\n```bash\nexport GITHUB_TOKEN=\"your_token\"\nexport ORG=\"your-org\"\nexport REPO_NAMES=\"repo1,repo2\"\nexport REPO_OWNERS=\"platform-team\"\n```\n\n**Usage:**\n```bash\ncd org-admin/github-auto-repo-creation\n./github-auto-repo-creation.sh\n```\n\n**What it does:**\n- Creates each repository listed in `REPO_NAMES` as private\n- Configures branch protection on the default branch\n- Creates a CODEOWNERS file referencing the `REPO_OWNERS` teams\n- Grants admin access to teams listed in `ADMIN_TEAMS`\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `ADMIN_TEAMS` | Comma-separated team slugs for admin access | — |\n\n\u003e [!NOTE]\n\u003e Requires `base64` in addition to `curl` and `jq` (used to encode the CODEOWNERS file content for the API).\n\n\u003e [!NOTE]\n\u003e 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.\n\n---\n\n### Close Archived Repository Security Alerts\n\n**Script:** `org-admin/github-close-archived-repo-security-alerts/github-close-archived-repo-security-alerts.sh`\n\nDismisses 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.\n\n**Required variables:**\n```bash\nexport GITHUB_TOKEN=\"your_token\"\nexport ORG=\"your-org\"\n```\n\n**Usage:**\n```bash\ncd org-admin/github-close-archived-repo-security-alerts\n\n# Preview which alerts would be dismissed (no changes)\n./github-close-archived-repo-security-alerts.sh --dry-run\n\n# Dismiss only Dependabot alerts\n./github-close-archived-repo-security-alerts.sh --type dependabot\n\n# Dismiss all alert types\n./github-close-archived-repo-security-alerts.sh\n```\n\n**Options:**\n\n| Flag | Description | Default |\n|------|-------------|---------|\n| `--type \u003ctype\u003e` | Alert type: `dependabot \\| code-scanning \\| secret-scanning \\| all` | `all` |\n| `--dry-run` | List alerts without dismissing them | — |\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `DEPENDABOT_REASON` | Dismiss reason for Dependabot alerts | `tolerable_risk` |\n| `CODE_SCANNING_REASON` | Dismiss reason for code scanning alerts | `won't fix` |\n| `SECRET_SCANNING_RESOLUTION` | Resolution for secret scanning alerts | `wont_fix` |\n\n**What it does:**\n- Enumerates all repositories in the organization\n- For each configured alert type, pages through all open alerts\n- Dismisses or resolves alerts with the configured reason\n- Generates a timestamped CSV report in the `reports/` subdirectory\n\n\u003e [!IMPORTANT]\n\u003e Requires a token with `security_events` and `repo` scope. Use `--dry-run` first to preview the scope of changes.\n\n---\n\n### Enable Issues\n\n**Script:** `org-admin/github-enable-issues/github-enable-issues.sh`\n\nEnables the Issues feature on every repository in a GitHub organisation that currently has it disabled. Skips archived repositories.\n\n**Required variables:**\n```bash\nexport GITHUB_TOKEN=\"your_token\"\nexport ORG=\"your-org\"\n```\n\n**Usage:**\n```bash\ncd org-admin/github-enable-issues\n./github-enable-issues.sh --dry-run   # Preview only\n./github-enable-issues.sh             # Apply changes\n```\n\n| Flag | Description |\n|------|-------------|\n| `--dry-run` | List repositories that would be updated without making changes |\n\n**What it does:**\n- Iterates all non-archived repositories in the organization (paginated)\n- Identifies repositories with Issues disabled\n- Enables Issues on each matching repository via the PATCH API\n\n---\n\n### Get Repository List\n\n**Script:** `org-admin/github-get-repo-list/github-get-repo-list.sh`\n\nLists all repositories in a GitHub organisation and outputs their metadata to stdout in CSV format.\n\n**Required variables:**\n```bash\nexport GITHUB_TOKEN=\"your_token\"\nexport ORG=\"your-org\"\n```\n\n**Usage:**\n```bash\ncd org-admin/github-get-repo-list\n./github-get-repo-list.sh\n./github-get-repo-list.sh \u003e repos.csv\n```\n\n**What it does:**\n- Fetches all repositories (paginated, 100 per page)\n- Outputs a CSV row per repository with: full name, owner, visibility, URL, description, fork flag, pushed/created/updated timestamps\n\n---\n\n### Migrate Internal Repositories to Private\n\n**Script:** `org-admin/github-migrate-internal-repos-to-private/github-migrate-internal-repos-to-private.sh`\n\nConverts all repositories with \"internal\" visibility to \"private\" in a GitHub organisation.\n\n**Required variables:**\n```bash\nexport GITHUB_TOKEN=\"your_token\"\nexport ORG=\"your-org\"\n```\n\n**Usage:**\n```bash\ncd org-admin/github-migrate-internal-repos-to-private\n./github-migrate-internal-repos-to-private.sh\n```\n\n**What it does:**\n- Fetches all repositories with internal visibility (paginated)\n- Converts each to private via the PATCH API\n- Logs success or failure for each repository\n\n\u003e [!WARNING]\n\u003e Converting repositories from internal to private removes access from members of other organisations in the enterprise. This operation cannot be undone via the API.\n\n---\n\n### Monthly Issues Report\n\n**Script:** `reporting/github-monthly-issues-report/github-monthly-issues-report.sh`\n\nGenerates HTML-formatted statistics about issues created within a date range, filtered by labels.\n\n**Required variables:**\n```bash\nexport GITHUB_TOKEN=\"your_token\"\nexport ORG=\"your-org\"\nexport REPO=\"your-repo\"\nexport MONTH_START=\"2025-10-01\"\nexport MONTH_END=\"2025-10-31\"\n```\n\n**Usage:**\n```bash\ncd reporting/github-monthly-issues-report\n./github-monthly-issues-report.sh\n```\n\n**What it does:**\n- Filters issues by creation date range\n- Filters by label (currently hardcoded to `Linked [AC]`)\n- Uses timeline API to track who applied labels\n- Generates author and contributor statistics\n- Outputs HTML report to `output.txt`\n\n\u003e [!NOTE]\n\u003e This script includes a hardcoded label filter. Edit the script to modify the filter criteria.\n\n---\n\n### Repository Permissions Report\n\n**Script:** `reporting/github-repo-permissions-report/github-repo-permissions-report.sh`\n\nExports repository user/team permissions to CSV and identifies who can bypass pull request approval requirements, from both branch protection rules and repository rulesets.\n\n**Prerequisites:**\n- **[curl](https://curl.se)** — HTTP client\n- **[jq](https://stedolan.github.io/jq)** — JSON processor\n\n**Usage:**\n```bash\nexport GITHUB_TOKEN=ghp_yourtoken   # or resolved automatically from an active gh auth session\ncd reporting/github-repo-permissions-report\n./github-repo-permissions-report.sh -r OWNER/REPO\n./github-repo-permissions-report.sh -r OWNER/REPO -b main -o report.csv\n```\n\n**Options:**\n\n| Flag | Description | Default |\n|------|-------------|----------|\n| `-r, --repo OWNER/REPO` | Target repository (required) | — |\n| `-b, --branch NAME` | Branch to evaluate | Repository default branch |\n| `-o, --output FILE` | Output CSV path | `OWNER-REPO-permissions-BRANCH-YYYYMMDD.csv` |\n\n**What it does:**\n- Fetches all collaborators and teams with repository access\n- Fetches branch protection rules and repository rulesets\n- Identifies every principal that can bypass PR approval requirements\n- Produces a CSV with two record types: `permission` (all users/teams) and `bypass_actor` (explicit bypass entries)\n\n---\n\n### Copilot Enterprise Report\n\n**Script:** `reporting/github-copilot-report/github-copilot-report.sh`\n\nGenerates a GitHub Copilot Enterprise licence and usage report. Shows every licensed user, their plan type, pool credit contribution, and actual AI credit consumption for the current billing month. Optionally enriches data with Entra ID department information.\n\n**Prerequisites:**\n- **[curl](https://curl.se)** — HTTP client\n- **[az](https://learn.microsoft.com/en-us/cli/azure/)** — Azure CLI (optional; required only for Entra ID department enrichment — pass `--no-entra` or omit az to skip)\n- **[jq](https://stedolan.github.io/jq)** — JSON processor\n\n**Required variables:**\n```bash\nexport GITHUB_ENTERPRISE=\"your-enterprise-slug\"\n\n# GitHub auth — use one of:\nexport GITHUB_TOKEN=ghp_yourtoken    # PAT with read:enterprise and manage_billing:enterprise scopes\n# OR: token is resolved automatically from an active gh auth session with the required scopes\n\n# Optional: Entra ID UPN domain for users without a public GitHub email\nexport UPN_DOMAIN=\"example.com\"        # e.g. 'john_example' → john@example.com\n\n# Optional: override the credits-per-seat value shown in your billing portal\nexport CREDITS_PER_SEAT_OVERRIDE=\"1900\"\n```\n\n**Usage:**\n```bash\ncd reporting/github-copilot-report\n\naz login   # optional; needed only for Entra ID department enrichment\n\n./github-copilot-report.sh -e YOUR-ENTERPRISE\n./github-copilot-report.sh -e YOUR-ENTERPRISE -d example.com\n./github-copilot-report.sh -e YOUR-ENTERPRISE --no-entra\n./github-copilot-report.sh -e YOUR-ENTERPRISE --credits 1900 --output report.csv\n```\n\n**Options:**\n\n| Flag | Description | Default |\n|------|-------------|---------|\n| `-e, --enterprise SLUG` | GitHub Enterprise slug (or `$GITHUB_ENTERPRISE`) | — |\n| `-d, --upn-domain DOM` | Email domain for Entra lookup when GitHub carries no email (or `$UPN_DOMAIN`) | — |\n| `--credits N` | Override credits-per-seat value (or `$CREDITS_PER_SEAT_OVERRIDE`) | Auto-detected |\n| `--output FILE` | Output CSV filename | `copilot-report-YYYYMMDD.csv` |\n| `--no-entra` | Skip Entra ID department lookup | — |\n\n**What it does:**\n- Fetches all Copilot seats across the enterprise (deduplicated by user)\n- Fetches per-user AI credit consumption for the current billing month\n- Fetches enterprise-level model usage metrics (last 28 days)\n- Optionally enriches each user with Entra ID department and job title via `az rest`\n- Outputs a CSV and a formatted console summary with department breakdown and model usage tables\n\n\u003e [!IMPORTANT]\n\u003e Requires a PAT with `read:enterprise` and `manage_billing:enterprise` scopes — the built-in `GITHUB_TOKEN` cannot grant enterprise-level access. Set `GITHUB_TOKEN` before executing (or have an active `gh` auth session with those scopes so the lib can auto-resolve the token).\n\n\u003e [!NOTE]\n\u003e The Entra ID enrichment is skipped automatically if `az` is not logged in, or can be disabled with `--no-entra`.\n\n---\n\n### Get Consumed Licenses\n\n**Script:** `enterprise/github-get-consumed-licenses/github-get-consumed-licenses.sh`\n\nRetrieves license consumption metrics for a GitHub Enterprise account.\n\n**Required variables:**\n```bash\nexport GITHUB_TOKEN=\"your_enterprise_token\"  # Must have read:enterprise scope\nexport ENTERPRISE=\"your-enterprise\"\n```\n\n**Usage:**\n```bash\ncd enterprise/github-get-consumed-licenses\n./github-get-consumed-licenses.sh\n```\n\n**Output:**\n```\nTotal seats consumed: 150\nTotal seats purchased: 200\n```\n\n**What it does:**\n- Calls the enterprise consumed-licenses endpoint\n- Uses Bearer token authentication (unlike other scripts)\n- Returns seat consumption and purchase counts\n\n\u003e [!IMPORTANT]\n\u003e This script requires an enterprise-level token with `read:enterprise` scope. Organization tokens will not work.\n\n---\n\n### Install Enterprise App\n\n**Script:** `enterprise/github-install-enterprise-app/github-install-enterprise-app.sh`\n\nProgrammatically installs an enterprise-owned \"automation\" GitHub App into an enterprise-owned organization, using a second enterprise-owned \"installer\" GitHub App that holds the **Enterprise organization installations** permission. Follows GitHub's [Automating app installations](https://docs.github.com/en/enterprise-cloud@latest/admin/managing-github-apps-for-your-enterprise/automate-installations) guide.\n\nAuthentication is performed entirely with the two GitHub Apps (JWT → installation access token). No `GITHUB_TOKEN` / PAT is used, and JWTs and access tokens are never printed.\n\n**Required variables:**\n```bash\nexport ENTERPRISE=\"my-enterprise\"\nexport ORG=\"my-org\"                                   # Target org for the install\nexport INSTALLER_APP_CLIENT_ID=\"Iv23li...\"            # Installer app client ID\nexport INSTALLER_APP_PRIVATE_KEY=\"~/installer-app.private-key.pem\"\nexport INSTALLER_APP_INSTALL_ID=\"12345678\"           # Installer app's enterprise install ID\nexport AUTOMATION_APP_CLIENT_ID=\"Iv23li...\"          # App to install in the org\n```\n\n**Usage:**\n```bash\ncd enterprise/github-install-enterprise-app\n./github-install-enterprise-app.sh           # Install the automation app\n./github-install-enterprise-app.sh --dry-run # Authenticate only; make no changes\n```\n\n**What it does:**\n- Generates a short-lived RS256 JWT from the installer app's client ID and private key (via `openssl`)\n- Exchanges the JWT for an enterprise-scoped installation access token\n- Installs the automation app in the target organization and prints the new installation ID\n- Optionally verifies the install by minting an org-scoped token when `AUTOMATION_APP_PRIVATE_KEY` is supplied\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `AUTOMATION_APP_PRIVATE_KEY` | Path to the automation app `.pem`; when set, verifies the new install by minting an org-scoped token | (unset — verification skipped) |\n| `REPO_SELECTION` | Repository access for the install: `all` or `selected` | `all` |\n| `API_URL_PREFIX` | GitHub API base URL | `https://api.github.com` |\n\n\u003e [!IMPORTANT]\n\u003e Requires two enterprise-owned GitHub Apps. The installer app must be installed on the enterprise account with read and write access to **Enterprise organization installations**. This script additionally requires `openssl` for JWT generation.\n\n---\n\n### Add Enterprise Team Read Permissions\n\n**Script:** `enterprise/github-add-enterprise-team-read-permissions/github-add-enterprise-team-read-permissions.sh`\n\nAssigns the built-in \"All-repository read\" organisation role to a specified enterprise team in every organisation within a GitHub Enterprise account. This grants read access to all current and future repositories without requiring per-repository assignments.\n\n**Required variables:**\n```bash\nexport GITHUB_TOKEN=\"your_enterprise_token\"   # Must have admin:enterprise scope\nexport ENTERPRISE=\"my-enterprise\"\nexport ENTERPRISE_TEAM_SLUG=\"platform-team\"\n```\n\n**Usage:**\n```bash\ncd enterprise/github-add-enterprise-team-read-permissions\n./github-add-enterprise-team-read-permissions.sh\n```\n\n**What it does:**\n- Enumerates all organizations in the enterprise via GraphQL (cursor-based pagination)\n- Looks up the enterprise team ID and the target org role ID in each organization\n- Assigns the `all_repo_read` org role to the enterprise team in every organization\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `ALL_REPO_READ_ROLE_NAME` | Org role name to assign | `all_repo_read` |\n\n\u003e [!IMPORTANT]\n\u003e Requires an enterprise-level token with `admin:enterprise` scope. Organization tokens will not work.\n\n---\n\n### Dockerfile Discovery\n\n**Script:** `enterprise/github-dockerfile-discovery/github-dockerfile-discovery.sh`\n\nSearches all organisations in a GitHub Enterprise account for Dockerfiles, extracts base image references from `FROM` instructions, and generates CSV reports to identify common base images across the estate.\n\n**Required variables:**\n```bash\nexport GITHUB_TOKEN=\"your_token\"\nexport ENTERPRISE=\"my-enterprise\"\n```\n\n**Usage:**\n```bash\ncd enterprise/github-dockerfile-discovery\n./github-dockerfile-discovery.sh\n```\n\n**What it does:**\n- Discovers all orgs in the enterprise via GraphQL, or uses the `ORGS` override\n- Uses the GitHub code search API to locate Dockerfiles in each org\n- Fetches and parses each Dockerfile to extract `FROM` instructions (including multi-stage builds)\n- Generates three timestamped reports in `REPORT_DIR`:\n  - `dockerfile_discovery_detail_*.csv` — one row per image reference\n  - `dockerfile_discovery_summary_*.csv` — image usage counts\n  - `dockerfile_discovery_summary_*.txt` — plain-text summary\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `REPORT_DIR` | Output directory for reports | `./reports` |\n| `ORGS` | Comma-separated org list; skips enterprise lookup | — |\n| `ORG_FILTER` | ERE regex to keep only matching org names | — |\n| `ORG_EXCLUDE` | ERE regex to drop matching org names | — |\n| `SEARCH_SLEEP` | Seconds between code-search requests | `2` |\n| `CONTENT_SLEEP` | Seconds between content-fetch requests | `1` |\n\n\u003e [!NOTE]\n\u003e Code search is heavily rate-limited. Increase `SEARCH_SLEEP` and `CONTENT_SLEEP` if you encounter `403` responses.\n\n\u003e [!NOTE]\n\u003e `ORG_FILTER` and `ORG_EXCLUDE` are validated as syntactically correct ERE patterns before use. An invalid regex causes the script to exit immediately with an error rather than silently bypassing the filter.\n\n---\n\n### Get Public Repositories\n\n**Script:** `enterprise/github-get-public-repos/github-get-public-repos.sh`\n\nLists all repositories with public visibility across every organisation in a GitHub Enterprise account and writes a timestamped CSV report.\n\n**Required variables:**\n```bash\nexport GITHUB_TOKEN=\"your_token\"\nexport ENTERPRISE=\"my-enterprise\"\n```\n\n**Usage:**\n```bash\ncd enterprise/github-get-public-repos\n./github-get-public-repos.sh\n./github-get-public-repos.sh \u003e public_repos.csv\n```\n\n**What it does:**\n- Discovers all orgs in the enterprise, or uses the `ORGS` override\n- Fetches all repositories in each org and filters to public visibility\n- Writes a timestamped CSV report in `REPORT_DIR` with: org, repo name, URL, description, created date, last pushed date\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `REPORT_DIR` | Output directory for reports | `./reports` |\n| `ORGS` | Comma-separated org list; skips enterprise lookup | — |\n| `ORG_FILTER` | ERE regex to keep only matching org names | — |\n| `ORG_EXCLUDE` | ERE regex to drop matching org names | — |\n\n\u003e [!NOTE]\n\u003e `ORG_FILTER` and `ORG_EXCLUDE` are validated as syntactically correct ERE patterns before use. An invalid regex causes the script to exit immediately with an error rather than silently bypassing the filter.\n\n---\n\n### Organize Starred Repositories\n\n**Script:** `personal/github-organize-stars/github-organize-stars.sh`\n\nFetches all your starred repositories and organizes them into GitHub Lists using customizable categorization rules.\n\n**Prerequisites:**\n- **[gh](https://cli.github.com)** - GitHub CLI (authenticated via `GITHUB_TOKEN` env var or `gh auth login`)\n- **[jq](https://stedolan.github.io/jq)** - Command-line JSON processor\n\n**Usage:**\n```bash\ncd personal/github-organize-stars\n./github-organize-stars.sh              # Interactive (shows plan, asks to confirm)\n./github-organize-stars.sh --dry-run    # Preview only, no changes made\n./github-organize-stars.sh -y           # Skip confirmation prompt\n./github-organize-stars.sh --show-repos # Also list repo names in each category\n./github-organize-stars.sh --no-cache   # Force re-fetch stars from GitHub\n```\n\n**What it does:**\n- Fetches all starred repositories via the GraphQL API (paginated)\n- Categorizes each repo by primary language, GitHub topics, and repo name keywords\n- Creates new GitHub Lists and adds repos in batches of 25\n- Caches fetched stars locally at `~/.cache/gh-star-organizer/stars.json` to speed up re-runs\n- Shows a categorization plan and prompts for confirmation before making changes\n\n**Customizing categories:**\n\nEdit the `RULES` array in the script. Each rule is a `|`-delimited string:\n```\n\"List Name|LANGUAGES|TOPICS|NAME_KEYWORDS\"\n```\n- `LANGUAGES` — comma-separated primary language names (case-insensitive)\n- `TOPICS` — comma-separated GitHub topic slugs\n- `NAME_KEYWORDS` — comma-separated substrings matched against the repo name\n\nThe **first matching rule wins**, so order matters. Place more specific rules (e.g., AI) before general ones (e.g., Security).\n\n\u003e [!NOTE]\n\u003e This script uses the `gh` CLI for all API calls (GraphQL) rather than `curl`. Authenticate by setting `GITHUB_TOKEN` or by running `gh auth login`.\n\n## Shared Library: `lib/github-common.sh`\n\nAll scripts can leverage a shared utility library for common operations like validation, colored output, and API helpers. The library is optional—scripts work standalone without it—but sourcing it reduces code duplication and adds helpful utilities.\n\n### Available Functions\n\n**Output formatting:**\n- `print_status \u003cmessage\u003e` — Blue INFO message\n- `print_success \u003cmessage\u003e` — Green SUCCESS message\n- `print_warning \u003cmessage\u003e` — Yellow WARNING message\n- `print_error \u003cmessage\u003e` — Red ERROR message (to stderr)\n\n**Validation:**\n- `require_env_var \u003cVAR_NAME\u003e [description]` — Exit if environment variable is empty\n- `require_command \u003ccmd\u003e [hint]` — Exit if command is not in PATH\n- `validate_token \u003cVAR_NAME\u003e [bearer]` — Validate GitHub token by calling `/user` endpoint\n- `validate_github_token [bearer]` — Convenience wrapper for `GITHUB_TOKEN` validation\n- `validate_slug \u003cvalue\u003e \u003clabel\u003e` — Exit if value contains characters other than alphanumeric, hyphen, or underscore\n\n**Auth helpers:**\n- `configure_gh_auth [scope_hint]` — Bridge `GITHUB_TOKEN→GH_TOKEN` for scripts that use the `gh` CLI, or verify an active `gh` auth session if no token is set\n\n**API helpers:**\n- `get_repo_page_count \u003curl\u003e` — Get total page count from paginated REST endpoint\n- `gh_api \u003cpath|url\u003e [curl args...]` — Bearer-auth REST helper with automatic rate-limit retry (up to 5 attempts); returns the literal string `__404__` or `__422__` (exit 0) for those status codes — callers must check for these sentinels before passing the result to `jq`\n- `gh_api_paginate \u003cpath\u003e [jq_filter] [api_version]` — Paginated REST helper that follows `Link` headers and streams items through `jq_filter` (default `.[]`); silently returns empty output on 404/422; pipe through `jq -s '.'` to collect all items as an array\n- `_paginate_orgs_endpoint \u003cjq_filter\u003e \u003curl_template\u003e` — Page through an org-list REST endpoint, printing one login per line; use `PAGE` as a placeholder in the URL template\n- `_graphql_enterprise_orgs` — Cursor-based GraphQL pagination for all orgs in `ENTERPRISE`; prints one login per line\n- `get_enterprise_orgs` — Three-tier enterprise org resolver: tries REST `/enterprises/{slug}/organizations`, falls back to GraphQL, then falls back to `/user/orgs`\n\n### Using the Shared Library in Scripts\n\nSource the library from your script:\n\n```bash\n#!/bin/bash\nset -euo pipefail\n\nGITHUB_TOKEN=${GITHUB_TOKEN:-''}\nORG=${ORG:-''}\nAPI_URL_PREFIX=${API_URL_PREFIX:-'https://api.github.com'}\n\n# Source shared library\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" \u0026\u0026 pwd)\"\n# shellcheck source=../../lib/github-common.sh\nsource \"${SCRIPT_DIR}/../../lib/github-common.sh\"\n\n# Now use library functions\nrequire_env_var GITHUB_TOKEN \"GitHub Personal Access Token\"\nrequire_env_var ORG \"GitHub Organization\"\nvalidate_github_token\n\nprint_status \"Processing organization: ${ORG}\"\n# ... rest of script\n```\n\n## Using Scripts in GitHub Actions\n\nEach script is published as a **composite action**, so you can reference it directly with a `uses:` step — no manual checkout required. Dependabot automatically tracks the pinned version and opens bump PRs when a new release is published.\n\n```yaml\n- uses: locus313/github-api-scripts/org-admin/github-add-repo-permissions@v1.0.0\n  with:\n    github-token: ${{ secrets.ORG_ADMIN_TOKEN }}\n    org: my-org\n    repo-push: developers maintainers\n```\n\n### Available Actions\n\n| Action | Description |\n|--------|-------------|\n| `enterprise/github-add-enterprise-team-read-permissions` | Grant read permissions to an enterprise team across all orgs |\n| `enterprise/github-dockerfile-discovery` | Discover Dockerfiles and extract `FROM` instructions across all enterprise orgs |\n| `enterprise/github-get-consumed-licenses` | Report Enterprise consumed licence seat counts |\n| `enterprise/github-get-public-repos` | List all public repositories across all Enterprise orgs |\n| `enterprise/github-install-enterprise-app` | Install an enterprise-owned GitHub App into an org (JWT flow) |\n| `org-admin/github-add-repo-collaborators-by-pattern` | Add collaborators to repositories matching a name pattern |\n| `org-admin/github-add-repo-permissions` | Grant team permissions across all repositories in an org |\n| `org-admin/github-archive-old-repos` | Archive repositories not updated within a configurable age threshold |\n| `org-admin/github-auto-repo-creation` | Create private repositories with branch protection and CODEOWNERS |\n| `org-admin/github-close-archived-repo-security-alerts` | Dismiss all open security alerts on archived repositories |\n| `org-admin/github-enable-issues` | Enable Issues on all repositories where it is currently disabled |\n| `org-admin/github-get-repo-list` | Output a CSV list of all repositories in an org |\n| `org-admin/github-import-repo` | Mirror-clone a repository into a new private org repository |\n| `org-admin/github-migrate-internal-repos-to-private` | Convert all internal-visibility repositories to private |\n| `org-admin/github-repo-from-template` | Create a repository from a template with team permissions and a CI/CD collaborator |\n| `reporting/github-monthly-issues-report` | Generate an HTML report of issues created within a date range |\n| `reporting/github-repo-permissions-report` | Export repository collaborator/team permissions and branch-approval bypass actors to CSV |\n| `reporting/github-copilot-report` | GitHub Copilot Enterprise licence and AI credit usage report, optionally enriched with Entra ID department data |\n\n---\n\n### Example 1: Grant Team Permissions on Repository Changes\n\nAutomatically update team permissions when ownership rules change:\n\n```yaml\nname: Update Repository Permissions\non:\n  push:\n    branches: [main]\n    paths:\n      - '.github/CODEOWNERS'\n\njobs:\n  update-permissions:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: locus313/github-api-scripts/org-admin/github-add-repo-permissions@v1.0.0\n        with:\n          github-token: ${{ secrets.ORG_ADMIN_TOKEN }}\n          org: my-org\n          repo-push: developers maintainers\n          repo-triage: support-team\n```\n\n### Example 2: Archive Old Repositories Monthly\n\nSchedule automated archival of stale repositories:\n\n```yaml\nname: Archive Old Repositories\non:\n  schedule:\n    - cron: '0 9 1 * *'\n\njobs:\n  archive-old:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: locus313/github-api-scripts/org-admin/github-archive-old-repos@v1.0.0\n        with:\n          github-token: ${{ secrets.ORG_ADMIN_TOKEN }}\n          org: my-org\n          years-threshold: '5'\n          auto-confirm: 'true'\n          report-dir: ./reports\n\n      - name: Upload report as artifact\n        if: always()\n        uses: actions/upload-artifact@v4\n        with:\n          name: archive-report\n          path: reports/\n          retention-days: 30\n```\n\n### Example 3: Create Repositories from Template\n\nAllow manual repository creation with pre-configured settings via workflow dispatch:\n\n```yaml\nname: Create New Repository from Template\non:\n  workflow_dispatch:\n    inputs:\n      repo-name:\n        description: 'Repository name'\n        required: true\n        type: string\n      repo-owner:\n        description: 'Team slug for admin permissions'\n        required: true\n        type: string\n\njobs:\n  create-repo:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: locus313/github-api-scripts/org-admin/github-repo-from-template@v1.0.0\n        with:\n          github-token: ${{ secrets.ORG_ADMIN_TOKEN }}\n          org: my-org\n          repo-name: ${{ github.event.inputs.repo-name }}\n          template-repo: template-repo\n          repo-admin: ${{ github.event.inputs.repo-owner }}\n          repo-write: developers\n          cd-username: github-actions[bot]\n          cd-github-token: ${{ secrets.CD_TOKEN }}\n```\n\n### Example 4: Weekly Dockerfile Discovery Report\n\nTrack base images across your enterprise and commit reports back to your own repository:\n\n```yaml\nname: Discover Dockerfiles in Enterprise\non:\n  schedule:\n    - cron: '0 12 * * 1'\n\njobs:\n  discover-dockerfiles:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout your repository\n        uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n\n      - uses: locus313/github-api-scripts/enterprise/github-dockerfile-discovery@v1.0.0\n        with:\n          github-token: ${{ secrets.ENTERPRISE_TOKEN }}\n          enterprise: my-enterprise\n          report-dir: ./reports\n\n      - name: Upload reports as artifacts\n        if: always()\n        uses: actions/upload-artifact@v4\n        with:\n          name: dockerfile-reports\n          path: reports/\n          retention-days: 60\n\n      - name: Commit reports to repository\n        if: success()\n        run: |\n          git add reports/ || true\n          git diff --quiet \u0026\u0026 git diff --staged --quiet || (\n            git config user.name \"github-actions[bot]\"\n            git config user.email \"github-actions[bot]@users.noreply.github.com\"\n            git commit -m \"chore: update dockerfile discovery reports\"\n            git push\n          )\n```\n\n\u003e [!NOTE]\n\u003e Example 4 checks out your own repository first (so `git push` commits to it). The composite action writes reports to the `report-dir` path inside your workspace, so `git add reports/` picks them up correctly.\n\n### Keeping Actions Up to Date\n\nBecause each script is a composite action, Dependabot tracks the pinned version tag and opens bump PRs automatically. Add the following to your `.github/dependabot.yml` (if not already present):\n\n```yaml\nversion: 2\nupdates:\n  - package-ecosystem: github-actions\n    directory: /\n    schedule:\n      interval: monthly\n```\n\n### GitHub Actions Best Practices\n\n**Use separate secrets for sensitive operations:**\n```yaml\nenv:\n  GITHUB_TOKEN: ${{ secrets.ORG_ADMIN_TOKEN }}      # For org-level changes\n  CD_GITHUB_TOKEN: ${{ secrets.CD_BOT_TOKEN }}      # For CD user operations\n```\n\n**Capture logs for audit trails:**\n```yaml\n- uses: locus313/github-api-scripts/org-admin/github-add-repo-permissions@v1.0.0\n  with:\n    github-token: ${{ secrets.ORG_ADMIN_TOKEN }}\n    org: my-org\n    repo-push: developers\n\n- name: Upload execution log\n  if: always()\n  uses: actions/upload-artifact@v4\n  with:\n    name: execution-logs\n    path: execution.log\n```\n\n**Use workflow_dispatch for manual operations:**\n```yaml\non:\n  workflow_dispatch:\n    inputs:\n      org:\n        description: 'Organization name'\n        required: true\n```\n\n**Protect destructive operations:**\nConsider adding approval steps or environment protection rules before running destructive scripts (like archive or delete operations).\n\n## Best Practices\n\n**Test on a test organization first**\nAlways validate on a non-production organization before running against production resources. Several scripts support a `--dry-run` flag (`github-close-archived-repo-security-alerts`, `github-enable-issues`, `github-organize-stars`) that previews changes without applying them.\n\n**Rate limiting**\nScripts include built-in delays (5 seconds between repository operations) to stay within GitHub's rate limits. For large organizations with hundreds of repos, expect longer execution times.\n\n**Audit trails**\nCapture output for compliance and troubleshooting:\n```bash\n./script.sh 2\u003e\u00261 | tee execution-$(date +%Y%m%d).log\n```\n\n**Team slugs vs display names**\nGitHub API uses team slugs (lowercase, hyphenated). Example: \"Platform Team\" → `platform-team`. Find team slugs in your organization settings or via the API.\n\n**GitHub Enterprise Server**\nThese scripts support GHES. Set custom endpoints:\n```bash\nexport API_URL_PREFIX=\"https://github.company.com/api/v3\"\nexport GIT_URL_PREFIX=\"https://github.company.com\"\n```\n\n## Resources\n\n- [GitHub REST API Documentation](https://docs.github.com/en/rest)\n- [Creating a personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token)\n- [jq Manual](https://stedolan.github.io/jq/manual/)\n- [GitHub API Rate Limits](https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting)\n- [GitHub Actions Documentation](https://docs.github.com/en/actions)\n\n## Contributing\n\nContributions are welcome! Please follow these steps:\n\n1. **Fork** this repo and create a branch using Conventional Commits naming: `git checkout -b feat/my-new-script`\n2. **Install the pre-commit hook:** `./install-hooks.sh`\n3. **Add your script** following the conventions in [AGENTS.md](AGENTS.md):\n   - Create `\u003cdomain\u003e/github-\u003cname\u003e/github-\u003cname\u003e.sh`\n   - Start with the `# ===` header and `set -euo pipefail`\n   - Source `lib/github-common.sh` and validate all inputs\n   - Create `action.yml` in the same directory (see existing actions for the pattern)\n4. **Update README.md** with the env var table, usage example, and a row in the Available Actions table\n5. **Run shellcheck:** `shellcheck --severity=warning --exclude=SC2034,SC1091 --shell=bash your-script.sh`\n6. **Test on a non-production org** before submitting\n7. **Commit using [Conventional Commits](https://www.conventionalcommits.org/)** — `CHANGELOG.md` is auto-generated from commit messages; do not edit it manually\n8. **Open a PR** — the PR template will guide you through the checklist\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flocus313%2Fgithub-api-scripts","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flocus313%2Fgithub-api-scripts","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flocus313%2Fgithub-api-scripts/lists"}