Skip to content

Commit 720f391

Browse files
committed
Add various GitHub management scripts
- Implemented `github-enable-issues.sh` to enable issues on all repositories in an organization that currently have them disabled. - Created `github-get-repo-list.sh` to fetch and list all repositories in a specified organization. - Developed `github-import-repo.sh` for importing a repository from one name to another within the same organization. - Added `github-migrate-internal-repos-to-private.sh` to convert internal repositories to private. - Introduced `github-repo-from-template.sh` to create a new repository from a specified template and manage permissions. - Created `github-organize-stars.sh` to fetch starred repositories and organize them into GitHub Lists based on predefined categories. - Implemented `github-monthly-issues-report.sh` to generate a report of issues created within a specified date range, including authors and contributors.
1 parent c16f40a commit 720f391

22 files changed

Lines changed: 70 additions & 192 deletions

File tree

‎.github/copilot-instructions.md‎

Lines changed: 7 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,27 @@
22

33
## Project Overview
44

5-
This is a collection of **standalone bash automation scripts** for GitHub organization and enterprise administration. Each script lives in its own directory with a single `.sh` file. Scripts use `curl` + `jq` to interact with the GitHub REST and GraphQL APIs. A shared utility library lives in `lib/github-common.sh`.
5+
This is a collection of **standalone bash automation scripts** for GitHub organization and enterprise administration. Scripts are grouped by domain folders (`org-admin/`, `enterprise/`, `reporting/`, `personal/`), and each script lives in its own subdirectory with a single `.sh` file. Scripts use `curl` + `jq` to interact with the GitHub REST and GraphQL APIs. A shared utility library lives in `lib/github-common.sh`.
66

77
**Key principle:** Scripts are independent, self-contained utilities. Each script is a complete, self-validating executable that manages its own error handling and input validation. Scripts source `lib/github-common.sh` for shared validation and output helpers where applicable.
88

99
## Architecture Pattern
1010

1111
### Directory Structure
1212
```
13-
github-<script-name>/
14-
└── github-<script-name>.sh # Single executable script
13+
<domain>/
14+
└── github-<script-name>/
15+
└── github-<script-name>.sh # Single executable script
1516
lib/
16-
└── github-common.sh # Shared utility functions
17+
└── github-common.sh # Shared utility functions
1718
```
1819

19-
Each script follows this exact naming convention. Scripts are NOT imported or called by each other.
20+
Each script follows the `github-<script-name>/github-<script-name>.sh` naming convention inside one domain folder. Scripts are NOT imported or called by each other.
2021

2122
### Shared Library: `lib/github-common.sh`
2223

2324
Provides reusable helpers sourced by scripts:
2425

25-
- **Color output:** `print_status` (blue INFO), `print_success` (green), `print_warning` (yellow), `print_error` (red, to stderr)
26-
- **Validation:** `require_env_var <VAR> [desc]` — exits with status 1 if empty; `require_command <cmd> [hint]` — exits if not in PATH
27-
- **Token validation:** `validate_token <VAR_NAME> [bearer]` — calls `/user` endpoint; `validate_github_token [bearer]` — convenience wrapper for `GITHUB_TOKEN`
28-
- **Pagination:** `get_repo_page_count <url>` — returns total page count from Link header
2926

3027
### Script Anatomy (Standardized Pattern)
3128

@@ -44,21 +41,14 @@ GIT_URL_PREFIX=${GIT_URL_PREFIX:-'https://github.com'}
4441

4542
# Source shared library (when available)
4643
# SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
47-
# source "${SCRIPT_DIR}/../lib/github-common.sh"
44+
# source "${SCRIPT_DIR}/../../lib/github-common.sh"
4845

4946
# Token validation block
5047
# Helper functions for pagination/processing
5148
# Main execution logic
5249
```
5350

5451
**Critical patterns:**
55-
- Use `${VARIABLE:-''}` for all env vars to provide empty defaults
56-
- Set `API_URL_PREFIX` and `GIT_URL_PREFIX` for GitHub Enterprise Server compatibility
57-
- **ALWAYS validate required variables** before any API calls — use `require_env_var` from lib or explicit `if [ -z "${VAR}" ]` checks
58-
- **ALWAYS validate GITHUB_TOKEN** via `validate_github_token` (lib) or the `/user` endpoint pattern
59-
- Use `curl -s -o /dev/null -w "%{http_code}"` pattern to check HTTP status codes
60-
- Exit with status code 1 on validation failures with descriptive error messages
61-
- Rate limiting delays vary by script: `sleep 5` for per-repo team operations, `sleep 2` for archive operations, `sleep 0.5` or `sleep 0.2` for lighter patch operations
6252

6353
## GitHub API Patterns
6454

@@ -81,26 +71,13 @@ For enterprise-level org iteration (GraphQL), use cursor-based pagination — se
8171
### Rate Limiting
8272

8373
Rate limiting delays are calibrated per operation type:
84-
- **Per-repo team permission assignments:** `sleep 5` (see `github-add-repo-permissions.sh`, `github-repo-from-template.sh`)
85-
- **Archive operations:** `sleep 2` (see `github-archive-old-repos.sh`)
86-
- **Lightweight patch operations:** `sleep 0.5` (see `github-enable-issues.sh`) or `sleep 0.2` (see `github-close-archived-repo-security-alerts.sh`)
87-
- **Code search + content fetch:** configurable `SEARCH_SLEEP` / `CONTENT_SLEEP` with exponential backoff (see `github-dockerfile-discovery.sh`, `github-get-public-repos.sh`)
8874

8975
### Authentication Headers
9076

91-
- `token` scheme for standard org/repo API: `-H "Authorization: token ${GITHUB_TOKEN}"`
92-
- `Bearer` scheme for enterprise endpoints and GraphQL: `-H "Authorization: Bearer ${GITHUB_TOKEN}"`
93-
- Some scripts use **both** (e.g., `github-dockerfile-discovery.sh`, `github-get-public-repos.sh`)
94-
- Include API version when specified: `-H "X-GitHub-Api-Version: 2022-11-28"`
9577

9678
### Accept Headers (Media Types)
9779

9880
Different endpoints require specific Accept headers:
99-
- Template generation: `application/vnd.github.baptiste-preview+json`
100-
- Timeline events: `application/vnd.github.mockingbird-preview+json`
101-
- Issue reactions: `application/vnd.github.mercy-preview+json`
102-
- Repository visibility: `application/vnd.github.nebula-preview+json`
103-
- Standard operations: `application/vnd.github.v3+json`
10481

10582
### Enterprise Org Lookup Strategy
10683

@@ -120,120 +97,36 @@ Scripts follow this validation sequence:
12097
## Script-Specific Behaviors
12198

12299
### `github-add-enterprise-team-read-permissions`
123-
- Assigns the "All-repository read" built-in org role to an enterprise team across all orgs in an enterprise
124-
- Uses **GraphQL** with cursor-based pagination to iterate enterprise organizations
125-
- Variables: `ENTERPRISE`, `ENTERPRISE_TEAM_SLUG`, `ALL_REPO_READ_ROLE_NAME` (default: `all_repo_read`)
126-
- Handles SAML SSO blocks gracefully (skips affected orgs with a warning)
127-
- Provides a summary of skipped and failed orgs at the end
128100

129101
### `github-add-repo-collaborators-by-pattern`
130-
- Adds individual GitHub users as collaborators to repositories whose names match a regex
131-
- Variables: `ORG`, `COLLABORATORS` (comma-separated usernames), `REPO_NAME_REGEX` (ERE regex)
132-
- Optional: `PERMISSION` (default: `push`), `REPO_EXCLUDE_REGEX`
133-
- Paginates through all org repos; applies both include and exclude regex filters
134-
- No explicit rate-limit delay between user additions
135102

136103
### `github-add-repo-permissions`
137-
- Grants team permissions to ALL repos in an org across 5 permission levels
138-
- Variables: `REPO_ADMIN`, `REPO_MAINTAIN`, `REPO_PUSH`, `REPO_TRIAGE`, `REPO_PULL` (all accept **space-separated team slugs**)
139-
- At least one permission level variable must be set
140-
- Uses pagination + `sleep 5` delay between repos
141-
- Loops through teams with helper function `apply_team_permissions()`
142104

143105
### `github-archive-old-repos`
144-
- Identifies repos not updated in the last N years (configurable via `YEARS_THRESHOLD`, default: `5`)
145-
- Generates a timestamped CSV report in `reports/old_repos_${TIMESTAMP}.csv`
146-
- **Interactive:** Prompts for confirmation before archiving; shows top 10 oldest repos first
147-
- Skips already-archived repos; uses `sleep 2` between archive operations
148-
- macOS (`date -v`) and Linux (`date -d`) compatible date arithmetic
149106

150107
### `github-auto-repo-creation`
151-
- Creates repos with branch protection, a `CODEOWNERS` file, and team assignments
152-
- Variables: `REPO_NAMES` (comma-separated), `REPO_OWNERS` (comma-separated team slugs with write), `ADMIN_TEAMS` (comma-separated team slugs with admin)
153-
- Creates repos as `private: true`, with issues enabled, no projects or wiki
154-
- Encodes CODEOWNERS content via `base64` for API upload
155-
- Enforces branch protection on `main`: admin enforcement, 1 required approval, code owner reviews required
156108

157109
### `github-close-archived-repo-security-alerts`
158-
- Dismisses Dependabot, code-scanning, and secret-scanning alerts on all archived repos in an org
159-
- Accepts CLI flags: `--type dependabot|code-scanning|secret-scanning|all` (default: all), `--dry-run`
160-
- Optional dismiss/resolve reasons: `DEPENDABOT_REASON`, `CODE_SCANNING_REASON`, `SECRET_SCANNING_RESOLUTION`
161-
- Generates timestamped CSV report in `reports/security_alerts_closed_${TIMESTAMP}.csv`
162-
- Uses `sleep 0.2` between alert closure operations
163110

164111
### `github-dockerfile-discovery`
165-
- Searches all orgs in a GitHub Enterprise for Dockerfiles; extracts base images from `FROM` instructions
166-
- Variables: `ENTERPRISE` (or `ORGS` to skip enterprise lookup), optional `ORG_FILTER` / `ORG_EXCLUDE` ERE regex
167-
- Configurable delays: `SEARCH_SLEEP` (default: `2`), `CONTENT_SLEEP` (default: `1`)
168-
- Parses multi-stage builds, `ARG` defaults, image digests, and `--platform` overrides
169-
- Generates two CSVs (detail + summary) and a human-readable TXT summary in `REPORT_DIR` (default: `./reports`)
170-
- Requires `base64` (file content decoding) and `python3` (URL-encoding)
171-
- Uses exponential backoff (up to 5 retries) for rate-limited requests
172112

173113
### `github-enable-issues`
174-
- Enables the Issues feature on every repo in an org that has it disabled
175-
- Accepts `--dry-run` flag
176-
- Skips archived repos and repos already with issues enabled
177-
- Uses `sleep 0.5` between patch operations
178-
- Reports four counters: enabled, errors, already-on, archived
179114

180115
### `github-get-consumed-licenses`
181-
- Queries enterprise seat license consumption
182-
- Uses **Bearer token** authentication; requires `read:enterprise` token scope
183-
- Calls `/enterprises/{ENTERPRISE}/consumed-licenses` endpoint
184-
- Outputs two metrics: seats consumed and seats purchased
185116

186117
### `github-get-public-repos`
187-
- Lists all public repos across every org in a GitHub Enterprise; writes a timestamped CSV
188-
- Variables: `ENTERPRISE` (or `ORGS` override), optional `ORG_FILTER` / `ORG_EXCLUDE`
189-
- Fetches all repos per org and filters by `visibility == "public"` in jq (avoids unreliable `?type=public` param)
190-
- Uses exponential backoff for rate-limited requests
191-
- Generates `public_repos_${TIMESTAMP}.csv` in `REPORT_DIR` (default: `./reports`)
192118

193119
### `github-get-repo-list`
194-
- Generates a CSV of all repos in an org with full metadata
195-
- Writes `repo-list.csv` (relative to working directory)
196-
- Makes an extra individual API call per repo to fetch full details — slow on large orgs
197120

198121
### `github-import-repo`
199-
- Imports a repo from one org to another using **bare clone + mirror push** (preserves full history)
200-
- Takes 2 positional args: `$1=SRC_REPO`, `$2=DEST_REPO`
201-
- Variables: `ORG` (destination), `OWNER_USERNAME` (granted admin on new repo)
202-
- Creates destination repo with `visibility: internal`
203-
- Uses `mktemp -d` for working directory; cleans up after push
204-
- Accepts HTTP 201 (created) or 422 (already exists) as success
205-
- Requires `git` in PATH
206122

207123
### `github-migrate-internal-repos-to-private`
208-
- Converts all internal repos in an org to private visibility
209-
- Queries only `?type=internal` repos; sends `visibility: private` PATCH to each
210-
- No rate-limit delay between updates
211124

212125
### `github-monthly-issues-report`
213-
- Generates an HTML report of issues created during a specified month
214-
- Variables: `ORG`, `REPO`, `MONTH_START`, `MONTH_END` (ISO format `YYYY-MM-DD`)
215-
- **Hardcoded label filter:** `Linked%20[AC]` (URL-encoded `Linked [AC]`)
216-
- Uses timeline API (`mockingbird-preview` header) to track who applied labels
217-
- Output: `output.txt` (HTML, appended); temp file `test.json` (cleaned up)
218-
- Aggregates and sorts issue authors and label contributors by count (descending)
219126

220127
### `github-organize-stars`
221-
- Fetches all starred repos and organizes them into GitHub Lists by category rules
222-
- **Uses `gh` CLI** for authentication (not `GITHUB_TOKEN` env var)
223-
- Accepts flags: `--dry-run`, `-y`/`--yes`, `--show-repos`, `--no-cache`
224-
- Categorization via ordered rules (language, topics, name keywords); first match wins
225-
- Caches stars in `~/.cache/gh-star-organizer/stars.json`; batch-adds repos in groups of 25
226-
- Requires `gh` (GitHub CLI) and `jq`
227128

228129
### `github-repo-from-template`
229-
- Creates a new repo from a template, sets team permissions, and auto-accepts the CD user invite
230-
- Variables: `ORG`, `TEMPLATE_REPO`, `CD_USERNAME`, `CD_GITHUB_TOKEN`; positional arg: `REPO_NAME`
231-
- `REPO_ADMIN` and `REPO_WRITE` are **space-separated lists** of team slugs
232-
- Uses `baptiste-preview` Accept header for template generation API
233-
- Creates repo with `private: true` and `include_all_branches: true`
234-
- **Dual-token workflow:** Main token for creation; `CD_GITHUB_TOKEN` for auto-accepting invite
235-
- Polls `/user/repository_invitations` up to 12 times (60-second timeout) to find the invitation
236-
- Uses `sleep 5` between team permission assignments
237130

238131
## Development Guidelines
239132

@@ -250,32 +143,10 @@ Scripts follow this validation sequence:
250143

251144
### Testing Approach
252145
- **Always test on a test organization first**
253-
- Several scripts support `--dry-run` — use it; others have no dry-run mode so handle with care
254-
- Use `2>&1 | tee execution.log` to capture output for auditing
255-
256146
### Variable Naming Conventions
257147
- `GITHUB_TOKEN` — main admin token
258-
- `ORG` — organization name
259-
- `ENTERPRISE` — enterprise account slug (from enterprise URL)
260-
- `ENTERPRISE_TEAM_SLUG` — enterprise team slug (without "ent:" prefix)
261-
- `REPO` — single repository name
262-
- `TEMPLATE_REPO` — source template repository
263-
- `CD_USERNAME` / `CD_GITHUB_TOKEN` — automation/CI user credentials
264-
- `MONTH_START` / `MONTH_END` — ISO date format `YYYY-MM-DD`
265-
- `ORGS` — comma-separated org list (overrides enterprise lookup)
266-
- `ORG_FILTER` / `ORG_EXCLUDE` — ERE regex for org name filtering
267-
- `REPORT_DIR` — output directory for report files (default: `./reports`)
268-
269148
### Dependencies
270149
- **bash 4+** (for modern features)
271-
- **curl** (API requests — all scripts)
272-
- **jq** (JSON parsing — all scripts)
273-
- **git** (`github-import-repo` only)
274-
- **gh** (GitHub CLI — `github-organize-stars` only)
275-
- **base64** (`github-auto-repo-creation`, `github-dockerfile-discovery`)
276-
- **python3** (`github-dockerfile-discovery` — URL-encoding)
277-
- **date** (`github-archive-old-repos` — BSD/GNU compatible)
278-
279150
Keep new scripts dependency-minimal; document any non-standard dependencies explicitly.
280151

281152
## Common Pitfalls

0 commit comments

Comments
 (0)