Skip to content

Commit c57e562

Browse files
committed
feat: enhance scripts with validation functions and improve reporting output
1 parent 7a22e3c commit c57e562

8 files changed

Lines changed: 390 additions & 22 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
**/reports/*.csv
66
**/reports/*.txt
77

8+
# Monthly issues report output
9+
output.txt
10+
811
# Python bytecode/cache
912
**/__pycache__/
1013
**/*.pyc

README.md

Lines changed: 342 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,239 @@ cd org-admin/github-import-repo
233233
234234
---
235235

236+
### Add Repository Collaborators by Pattern
237+
238+
**Script:** `org-admin/github-add-repo-collaborators-by-pattern/github-add-repo-collaborators-by-pattern.sh`
239+
240+
Adds one or more individual collaborators to all repositories in an organisation whose names match a given regex pattern.
241+
242+
**Required variables:**
243+
```bash
244+
export GITHUB_TOKEN="your_token"
245+
export ORG="your-org"
246+
export COLLABORATORS="alice,bob"
247+
export REPO_NAME_REGEX='^service-'
248+
```
249+
250+
**Usage:**
251+
```bash
252+
cd org-admin/github-add-repo-collaborators-by-pattern
253+
./github-add-repo-collaborators-by-pattern.sh
254+
```
255+
256+
| Variable | Description | Default |
257+
|----------|-------------|---------|
258+
| `PERMISSION` | Permission level: `pull\|triage\|push\|maintain\|admin` | `push` |
259+
| `REPO_EXCLUDE_REGEX` | ERE regex to exclude matching repository names ||
260+
261+
**What it does:**
262+
- Iterates all repositories in the organization (paginated)
263+
- Filters to repos matching `REPO_NAME_REGEX`
264+
- Optionally excludes repos matching `REPO_EXCLUDE_REGEX`
265+
- Adds each specified collaborator with the configured permission level
266+
267+
---
268+
269+
### Archive Old Repositories
270+
271+
**Script:** `org-admin/github-archive-old-repos/github-archive-old-repos.sh`
272+
273+
Identifies and archives repositories that have not been updated within a configurable number of years. Generates a timestamped CSV report.
274+
275+
**Required variables:**
276+
```bash
277+
export GITHUB_TOKEN="your_token"
278+
export ORG="your-org"
279+
```
280+
281+
**Usage:**
282+
```bash
283+
cd org-admin/github-archive-old-repos
284+
./github-archive-old-repos.sh
285+
```
286+
287+
**What it does:**
288+
- Fetches all repositories in the organization (paginated)
289+
- Calculates a cutoff date based on `YEARS_THRESHOLD`
290+
- Generates a timestamped CSV report in the `reports/` subdirectory
291+
- Displays a summary with the top 10 oldest repositories
292+
- Prompts for confirmation before archiving
293+
- Archives qualifying repositories via the GitHub API, skipping already-archived ones
294+
295+
| Variable | Description | Default |
296+
|----------|-------------|---------|
297+
| `YEARS_THRESHOLD` | Age threshold in years | `5` |
298+
299+
---
300+
301+
### Auto Repository Creation
302+
303+
**Script:** `org-admin/github-auto-repo-creation/github-auto-repo-creation.sh`
304+
305+
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.
306+
307+
**Required variables:**
308+
```bash
309+
export GITHUB_TOKEN="your_token"
310+
export ORG="your-org"
311+
export REPO_NAMES="repo1,repo2"
312+
export REPO_OWNERS="platform-team"
313+
```
314+
315+
**Usage:**
316+
```bash
317+
cd org-admin/github-auto-repo-creation
318+
./github-auto-repo-creation.sh
319+
```
320+
321+
**What it does:**
322+
- Creates each repository listed in `REPO_NAMES` as private
323+
- Configures branch protection on the default branch
324+
- Creates a CODEOWNERS file referencing the `REPO_OWNERS` teams
325+
- Grants admin access to teams listed in `ADMIN_TEAMS`
326+
327+
| Variable | Description | Default |
328+
|----------|-------------|---------|
329+
| `ADMIN_TEAMS` | Comma-separated team slugs for admin access ||
330+
331+
> [!NOTE]
332+
> Requires `base64` in addition to `curl` and `jq` (used to encode the CODEOWNERS file content for the API).
333+
334+
---
335+
336+
### Close Archived Repository Security Alerts
337+
338+
**Script:** `org-admin/github-close-archived-repo-security-alerts/github-close-archived-repo-security-alerts.sh`
339+
340+
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.
341+
342+
**Required variables:**
343+
```bash
344+
export GITHUB_TOKEN="your_token"
345+
export ORG="your-org"
346+
```
347+
348+
**Usage:**
349+
```bash
350+
cd org-admin/github-close-archived-repo-security-alerts
351+
352+
# Preview which alerts would be dismissed (no changes)
353+
./github-close-archived-repo-security-alerts.sh --dry-run
354+
355+
# Dismiss only Dependabot alerts
356+
./github-close-archived-repo-security-alerts.sh --type dependabot
357+
358+
# Dismiss all alert types
359+
./github-close-archived-repo-security-alerts.sh
360+
```
361+
362+
**Options:**
363+
364+
| Flag | Description | Default |
365+
|------|-------------|---------|
366+
| `--type <type>` | Alert type: `dependabot \| code-scanning \| secret-scanning \| all` | `all` |
367+
| `--dry-run` | List alerts without dismissing them ||
368+
369+
| Variable | Description | Default |
370+
|----------|-------------|---------|
371+
| `DEPENDABOT_REASON` | Dismiss reason for Dependabot alerts | `tolerable_risk` |
372+
| `CODE_SCANNING_REASON` | Dismiss reason for code scanning alerts | `won't fix` |
373+
| `SECRET_SCANNING_RESOLUTION` | Resolution for secret scanning alerts | `wont_fix` |
374+
375+
**What it does:**
376+
- Enumerates all repositories in the organization
377+
- For each configured alert type, pages through all open alerts
378+
- Dismisses or resolves alerts with the configured reason
379+
- Generates a timestamped CSV report in the `reports/` subdirectory
380+
381+
> [!IMPORTANT]
382+
> Requires a token with `security_events` and `repo` scope. Use `--dry-run` first to preview the scope of changes.
383+
384+
---
385+
386+
### Enable Issues
387+
388+
**Script:** `org-admin/github-enable-issues/github-enable-issues.sh`
389+
390+
Enables the Issues feature on every repository in a GitHub organisation that currently has it disabled. Skips archived repositories.
391+
392+
**Required variables:**
393+
```bash
394+
export GITHUB_TOKEN="your_token"
395+
export ORG="your-org"
396+
```
397+
398+
**Usage:**
399+
```bash
400+
cd org-admin/github-enable-issues
401+
./github-enable-issues.sh --dry-run # Preview only
402+
./github-enable-issues.sh # Apply changes
403+
```
404+
405+
| Flag | Description |
406+
|------|-------------|
407+
| `--dry-run` | List repositories that would be updated without making changes |
408+
409+
**What it does:**
410+
- Iterates all non-archived repositories in the organization (paginated)
411+
- Identifies repositories with Issues disabled
412+
- Enables Issues on each matching repository via the PATCH API
413+
414+
---
415+
416+
### Get Repository List
417+
418+
**Script:** `org-admin/github-get-repo-list/github-get-repo-list.sh`
419+
420+
Lists all repositories in a GitHub organisation and outputs their metadata to stdout in CSV format.
421+
422+
**Required variables:**
423+
```bash
424+
export GITHUB_TOKEN="your_token"
425+
export ORG="your-org"
426+
```
427+
428+
**Usage:**
429+
```bash
430+
cd org-admin/github-get-repo-list
431+
./github-get-repo-list.sh
432+
./github-get-repo-list.sh > repos.csv
433+
```
434+
435+
**What it does:**
436+
- Fetches all repositories (paginated, 100 per page)
437+
- Outputs a CSV row per repository with: full name, owner, visibility, URL, description, fork flag, pushed/created/updated timestamps
438+
439+
---
440+
441+
### Migrate Internal Repositories to Private
442+
443+
**Script:** `org-admin/github-migrate-internal-repos-to-private/github-migrate-internal-repos-to-private.sh`
444+
445+
Converts all repositories with "internal" visibility to "private" in a GitHub organisation.
446+
447+
**Required variables:**
448+
```bash
449+
export GITHUB_TOKEN="your_token"
450+
export ORG="your-org"
451+
```
452+
453+
**Usage:**
454+
```bash
455+
cd org-admin/github-migrate-internal-repos-to-private
456+
./github-migrate-internal-repos-to-private.sh
457+
```
458+
459+
**What it does:**
460+
- Fetches all repositories with internal visibility (paginated)
461+
- Converts each to private via the PATCH API
462+
- Logs success or failure for each repository
463+
464+
> [!WARNING]
465+
> Converting repositories from internal to private removes access from members of other organisations in the enterprise. This operation cannot be undone via the API.
466+
467+
---
468+
236469
### Monthly Issues Report
237470

238471
**Script:** `reporting/github-monthly-issues-report/github-monthly-issues-report.sh`
@@ -386,6 +619,113 @@ Total seats purchased: 200
386619
387620
---
388621

622+
### Add Enterprise Team Read Permissions
623+
624+
**Script:** `enterprise/github-add-enterprise-team-read-permissions/github-add-enterprise-team-read-permissions.sh`
625+
626+
Assigns 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.
627+
628+
**Required variables:**
629+
```bash
630+
export GITHUB_TOKEN="your_enterprise_token" # Must have admin:enterprise scope
631+
export ENTERPRISE="my-enterprise"
632+
export ENTERPRISE_TEAM_SLUG="platform-team"
633+
```
634+
635+
**Usage:**
636+
```bash
637+
cd enterprise/github-add-enterprise-team-read-permissions
638+
./github-add-enterprise-team-read-permissions.sh
639+
```
640+
641+
**What it does:**
642+
- Enumerates all organizations in the enterprise via GraphQL (cursor-based pagination)
643+
- Looks up the enterprise team ID and the target org role ID in each organization
644+
- Assigns the `all_repo_read` org role to the enterprise team in every organization
645+
646+
| Variable | Description | Default |
647+
|----------|-------------|---------|
648+
| `ALL_REPO_READ_ROLE_NAME` | Org role name to assign | `all_repo_read` |
649+
650+
> [!IMPORTANT]
651+
> Requires an enterprise-level token with `admin:enterprise` scope. Organization tokens will not work.
652+
653+
---
654+
655+
### Dockerfile Discovery
656+
657+
**Script:** `enterprise/github-dockerfile-discovery/github-dockerfile-discovery.sh`
658+
659+
Searches 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.
660+
661+
**Required variables:**
662+
```bash
663+
export GITHUB_TOKEN="your_token"
664+
export ENTERPRISE="my-enterprise"
665+
```
666+
667+
**Usage:**
668+
```bash
669+
cd enterprise/github-dockerfile-discovery
670+
./github-dockerfile-discovery.sh
671+
```
672+
673+
**What it does:**
674+
- Discovers all orgs in the enterprise via GraphQL, or uses the `ORGS` override
675+
- Uses the GitHub code search API to locate Dockerfiles in each org
676+
- Fetches and parses each Dockerfile to extract `FROM` instructions (including multi-stage builds)
677+
- Generates three timestamped reports in `REPORT_DIR`:
678+
- `dockerfile_discovery_detail_*.csv` — one row per image reference
679+
- `dockerfile_discovery_summary_*.csv` — image usage counts
680+
- `dockerfile_discovery_summary_*.txt` — plain-text summary
681+
682+
| Variable | Description | Default |
683+
|----------|-------------|---------|
684+
| `REPORT_DIR` | Output directory for reports | `./reports` |
685+
| `ORGS` | Comma-separated org list; skips enterprise lookup ||
686+
| `ORG_FILTER` | ERE regex to keep only matching org names ||
687+
| `ORG_EXCLUDE` | ERE regex to drop matching org names ||
688+
| `SEARCH_SLEEP` | Seconds between code-search requests | `2` |
689+
| `CONTENT_SLEEP` | Seconds between content-fetch requests | `1` |
690+
691+
> [!NOTE]
692+
> Code search is heavily rate-limited. Increase `SEARCH_SLEEP` and `CONTENT_SLEEP` if you encounter `403` responses.
693+
694+
---
695+
696+
### Get Public Repositories
697+
698+
**Script:** `enterprise/github-get-public-repos/github-get-public-repos.sh`
699+
700+
Lists all repositories with public visibility across every organisation in a GitHub Enterprise account and writes a timestamped CSV report.
701+
702+
**Required variables:**
703+
```bash
704+
export GITHUB_TOKEN="your_token"
705+
export ENTERPRISE="my-enterprise"
706+
```
707+
708+
**Usage:**
709+
```bash
710+
cd enterprise/github-get-public-repos
711+
./github-get-public-repos.sh
712+
./github-get-public-repos.sh > public_repos.csv
713+
```
714+
715+
**What it does:**
716+
- Discovers all orgs in the enterprise, or uses the `ORGS` override
717+
- Fetches all repositories in each org and filters to public visibility
718+
- Writes a timestamped CSV report in `REPORT_DIR` with: org, repo name, URL, description, created date, last pushed date
719+
720+
| Variable | Description | Default |
721+
|----------|-------------|---------|
722+
| `REPORT_DIR` | Output directory for reports | `./reports` |
723+
| `ORGS` | Comma-separated org list; skips enterprise lookup ||
724+
| `ORG_FILTER` | ERE regex to keep only matching org names ||
725+
| `ORG_EXCLUDE` | ERE regex to drop matching org names ||
726+
727+
---
728+
389729
### Organize Starred Repositories
390730

391731
**Script:** `personal/github-organize-stars/github-organize-stars.sh`
@@ -448,6 +788,7 @@ All scripts can leverage a shared utility library for common operations like val
448788

449789
**API helpers:**
450790
- `get_repo_page_count <url>` — Get total page count from paginated REST endpoint
791+
- `validate_slug <value> <label>` — Exit if value contains characters other than alphanumeric, hyphen, or underscore
451792

452793
### Using the Shared Library in Scripts
453794

@@ -668,7 +1009,7 @@ Consider adding approval steps or environment protection rules before running de
6681009
## Best Practices
6691010
6701011
**Test on a test organization first**
671-
These scripts have no dry-run mode. Always validate on a non-production organization before running against production resources.
1012+
Always 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.
6721013

6731014
**Rate limiting**
6741015
Scripts 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.

enterprise/github-dockerfile-discovery/github-dockerfile-discovery.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -373,8 +373,9 @@ parse_dockerfile_content() {
373373
arg_default=$(echo "${arg_line}" | sed -E 's/^ARG[[:space:]]+[A-Za-z_][A-Za-z0-9_]*=?//')
374374
if [ -n "${arg_default}" ]; then
375375
# Escape special chars in default for use in sed replacement
376+
# Strip newlines and escape &, /, \, and ; to prevent sed script injection
376377
local safe_default
377-
safe_default=$(printf '%s' "${arg_default}" | sed 's/[&/\]/\\&/g')
378+
safe_default=$(printf '%s' "${arg_default}" | tr -d '\n' | sed 's/[&/\;]/\\&/g')
378379
arg_sed_script="${arg_sed_script}s/\\\${${arg_name}}/${safe_default}/g;"
379380
arg_sed_script="${arg_sed_script}s/\\\$${arg_name}\b/${safe_default}/g;"
380381
fi

0 commit comments

Comments
 (0)