Skip to content

Commit 6d69ad9

Browse files
locus313Copilot
andcommitted
fix: ponytail audit — remove ~330 lines of redundant code
- lib: add err(), --api-version param to gh_api() - close-archived-alerts: collapse 3 close_*_alerts() into 1 generic close_alerts(); replace hand-rolled pagination with gh_api_paginate - copilot-report: collapse _copilot_api() 30→3 lines using --api-version; remove info()/warn() pass-through aliases - repo-permissions-report: remove info() pass-through alias - dockerfile-discovery: delete dead gh_api_link() + RESPONSE_LINK global; remove vestigial print_* redefines; inline check_prerequisites() - get-public-repos: inline check_prerequisites(); drop no-op print_error override - 7 pagination scripts: remove local get_repo_pagination() wrappers, use lib get_repo_page_count() directly - organize-stars: replace 7-line command check loop with require_command - archive-old-repos: inline validate_environment(); move local declarations to assignment site; drop stale comments - auto-repo-creation: merge add_teams_to_repo + add_repo_owners_to_repo into single add_team_to_repo with permission param - docs: update AGENTS.md and copilot-instructions.md with err(), --api-version, correct pagination example, and fill blank shared-library section Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 6e5ad61 commit 6d69ad9

17 files changed

Lines changed: 149 additions & 483 deletions

File tree

.github/copilot-instructions.md

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,19 @@ Each script follows the `github-<script-name>/github-<script-name>.sh` naming co
2323

2424
Provides reusable helpers sourced by scripts:
2525

26+
| Function | Purpose |
27+
|----------|---------|
28+
| `print_status` / `print_success` / `print_warning` / `print_error` | Colored output |
29+
| `err <message>` | `print_error` + `exit 1` in one call |
30+
| `require_env_var <VAR>` | Exit with message if variable unset/empty |
31+
| `require_command <cmd>` | Exit if binary not in PATH |
32+
| `configure_gh_auth [scope_hint]` | Bridge GITHUB_TOKEN→GH_TOKEN or verify gh auth session |
33+
| `validate_github_token [bearer]` | Verify GITHUB_TOKEN via /user endpoint |
34+
| `validate_slug <value> <label>` | Reject values with non-alphanumeric/hyphen/underscore chars |
35+
| `gh_api <path> [--api-version V] [curl args...]` | Bearer-auth REST helper with 5-retry rate-limit handling; optional `--api-version` overrides the default `2022-11-28` header; returns literal `__404__` or `__422__` for those HTTP statuses — callers must check for these sentinels |
36+
| `gh_api_paginate <path> [filter] [version]` | Paginated REST helper, follows Link headers, streams items; returns silently with empty output on 404/422 |
37+
| `get_enterprise_orgs` | Three-tier enterprise org resolver (REST → GraphQL → /user/orgs) |
38+
| `get_repo_page_count <url>` | Returns total pages for a paginated endpoint |
2639

2740
### Script Anatomy (Standardized Pattern)
2841

@@ -84,12 +97,7 @@ API_URL_PREFIX=${API_URL_PREFIX:-'https://api.github.com'}
8497

8598
For org-level repo iteration (REST):
8699
```bash
87-
get_repo_pagination () {
88-
repo_pages=$(curl -H "Authorization: token ${GITHUB_TOKEN}" -I "${API_URL_PREFIX}/orgs/${ORG}/repos?per_page=100" | grep -Eo '&page=[0-9]+' | grep -Eo '[0-9]+' | tail -1;)
89-
echo "${repo_pages:-1}"
90-
}
91-
92-
for PAGE in $(seq "$(get_repo_pagination)"); do
100+
for PAGE in $(seq "$(get_repo_page_count "${API_URL_PREFIX}/orgs/${ORG}/repos?per_page=100")"); do
93101
# Process repos on this page with per_page=100
94102
done
95103
```
@@ -101,7 +109,7 @@ For enterprise-level org iteration (GraphQL), use cursor-based pagination — se
101109
Rate limiting delays are calibrated per operation type:
102110
- **Repo-level operations** (permission grants, archival): `sleep 5` between each repository.
103111
- **Code search** (`github-dockerfile-discovery`): configurable via `SEARCH_SLEEP` (default 2 s) and `CONTENT_SLEEP` (default 1 s).
104-
- **gh_api helper** (lib): auto-retries up to 5 times on HTTP 403/429, sleeping 60 s before each retry. Returns the literal string `__404__` or `__422__` (exit 0) for those HTTP statuses instead of failing — every caller must check for these sentinels before passing the result to `jq`. `gh_api_paginate` returns silently with empty output on 404/422.
112+
- **gh_api helper** (lib): auto-retries up to 5 times on HTTP 403/429, sleeping 60 s before each retry. Pass `--api-version VERSION` immediately after the URL to override the default `2022-11-28` header. Returns the literal string `__404__` or `__422__` (exit 0) for those HTTP statuses instead of failing — every caller must check for these sentinels before passing the result to `jq`. `gh_api_paginate` returns silently with empty output on 404/422.
105113

106114
### Authentication Headers
107115

AGENTS.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,12 +140,13 @@ Always use `SCRIPT_DIR` to build the path to `lib/github-common.sh`. The library
140140
| Function | Purpose |
141141
|----------|---------|
142142
| `print_status` / `print_success` / `print_warning` / `print_error` | Colored output |
143+
| `err <message>` | `print_error` + `exit 1` in one call |
143144
| `require_env_var <VAR>` | Exit with message if variable unset/empty |
144145
| `require_command <cmd>` | Exit if binary not in PATH |
145146
| `configure_gh_auth [scope_hint]` | Bridge GITHUB_TOKEN→GH_TOKEN or verify gh auth session |
146147
| `validate_github_token [bearer]` | Verify GITHUB_TOKEN via /user endpoint |
147148
| `validate_slug <value> <label>` | Reject values with non-alphanumeric/hyphen/underscore chars |
148-
| `gh_api <path> [curl args...]` | Bearer-auth REST helper with 5-retry rate-limit handling; returns literal `__404__` or `__422__` for those HTTP statuses — callers must check for these sentinels |
149+
| `gh_api <path> [--api-version V] [curl args...]` | Bearer-auth REST helper with 5-retry rate-limit handling; optional `--api-version` overrides the default `2022-11-28` header; returns literal `__404__` or `__422__` for those HTTP statuses — callers must check for these sentinels |
149150
| `gh_api_paginate <path> [filter] [version]` | Paginated REST helper, follows Link headers, streams items; returns silently with empty output on 404/422 |
150151
| `get_enterprise_orgs` | Three-tier enterprise org resolver (REST → GraphQL → /user/orgs) |
151152
| `get_repo_page_count <url>` | Returns total pages for a paginated endpoint |

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

Lines changed: 4 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,6 @@ set -euo pipefail
3232
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
3333
# shellcheck source=../../lib/github-common.sh
3434
source "${SCRIPT_DIR}/../../lib/github-common.sh"
35-
# Redirect status output to stderr — stdout is reserved for CSV data
36-
print_status() { echo -e "${BLUE}[INFO]${NC} $1" >&2; }
37-
print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1" >&2; }
38-
print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1" >&2; }
39-
print_error() { echo -e "${RED}[ERROR]${NC} $1" >&2; }
4035

4136
###
4237
## GLOBAL VARIABLES
@@ -68,60 +63,6 @@ cleanup() {
6863
}
6964
trap cleanup EXIT
7065

71-
###
72-
## Prerequisite checks
73-
###
74-
check_prerequisites() {
75-
print_status "Checking prerequisites..."
76-
require_command curl
77-
require_command jq
78-
require_command base64
79-
require_env_var GITHUB_TOKEN "GITHUB_TOKEN"
80-
print_success "Prerequisites OK"
81-
}
82-
83-
###
84-
## gh_api_link – like gh_api but also returns the Link header for pagination
85-
## Writes body to stdout, sets global RESPONSE_LINK
86-
###
87-
RESPONSE_LINK=""
88-
gh_api_link() {
89-
local url="$1"
90-
shift
91-
[[ "${url}" == http* ]] || url="${API_URL_PREFIX}${url}"
92-
93-
local attempt
94-
for attempt in 1 2 3 4 5; do
95-
local response
96-
response=$(curl -s -D - \
97-
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
98-
-H "Accept: application/vnd.github+json" \
99-
-H "X-GitHub-Api-Version: 2022-11-28" \
100-
"$@" "${url}")
101-
102-
local http_code
103-
http_code=$(echo "${response}" | head -1 | grep -oE '[0-9]{3}' | head -1)
104-
RESPONSE_LINK=$(echo "${response}" | grep -i '^Link:' | tr -d '\r' | sed 's/Link: //' || true)
105-
local body
106-
body=$(echo "${response}" | awk 'BEGIN{p=0} /^\r?$/{p=1; next} p{print}')
107-
108-
if [[ "${http_code}" == "200" ]]; then
109-
echo "${body}"
110-
return 0
111-
elif [[ "${http_code}" == "403" || "${http_code}" == "429" ]]; then
112-
local retry_after=60
113-
print_warning "Rate limited (HTTP ${http_code}). Sleeping ${retry_after}s..."
114-
sleep "${retry_after}"
115-
else
116-
print_warning "HTTP ${http_code} for ${url} (attempt ${attempt}/5)"
117-
sleep 5
118-
fi
119-
done
120-
121-
print_error "Failed to GET ${url} after 5 attempts"
122-
return 1
123-
}
124-
12566
###
12667
## search_dockerfiles_in_org <org>
12768
## Appends TSV rows (org TAB repo_full_name TAB path TAB html_url) to REFS_TEMP
@@ -383,7 +324,10 @@ build_text_summary() {
383324
## MAIN
384325
###
385326
main() {
386-
check_prerequisites
327+
require_command curl
328+
require_command jq
329+
require_command base64
330+
require_env_var GITHUB_TOKEN "GITHUB_TOKEN"
387331
validate_github_token
388332

389333
mkdir -p "${REPORT_DIR}"

enterprise/github-get-public-repos/github-get-public-repos.sh

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ source "${SCRIPT_DIR}/../../lib/github-common.sh"
3333
print_status() { echo -e "${BLUE}[INFO]${NC} $1" >&2; }
3434
print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1" >&2; }
3535
print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1" >&2; }
36-
print_error() { echo -e "${RED}[ERROR]${NC} $1" >&2; }
3736

3837
###
3938
## GLOBAL VARIABLES
@@ -60,17 +59,6 @@ cleanup() {
6059
}
6160
trap cleanup EXIT
6261

63-
###
64-
## Prerequisite checks
65-
###
66-
check_prerequisites() {
67-
print_status "Checking prerequisites..."
68-
require_command curl
69-
require_command jq
70-
require_env_var GITHUB_TOKEN "GITHUB_TOKEN"
71-
print_success "Prerequisites OK"
72-
}
73-
7462
###
7563
## resolve_orgs
7664
## Returns the final deduplicated list of org logins to scan.
@@ -186,7 +174,9 @@ get_public_repos_for_org() {
186174
## Main
187175
###
188176
main() {
189-
check_prerequisites
177+
require_command curl
178+
require_command jq
179+
require_env_var GITHUB_TOKEN "GITHUB_TOKEN"
190180
validate_github_token
191181

192182
mkdir -p "${REPORT_DIR}"

lib/github-common.sh

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
# validate_github_token [bearer] — verify GITHUB_TOKEN via /user endpoint
1919
# validate_token <VAR_NAME> — verify a secondary token variable
2020
# validate_slug <value> [label] — exit if value contains unsafe chars
21-
# gh_api <path|url> [curl args...] — Bearer-auth REST helper with retry;
21+
# gh_api <path|url> [--api-version V] [curl args…] — Bearer-auth REST helper with retry;
2222
# returns "__404__"/"__422__" (exit 0) for those codes
2323
# gh_api_paginate <path> [filter] [version] — paginated REST, follows Link headers;
2424
# silently returns empty output on 404/422
@@ -50,6 +50,7 @@ print_status() { echo -e "${BLUE}[INFO]${NC} $1"; }
5050
print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
5151
print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
5252
print_error() { echo -e "${RED}[ERROR]${NC} $1" >&2; }
53+
err() { print_error "$*"; exit 1; }
5354

5455
###
5556
## require_env_var <VAR_NAME> [description]
@@ -167,15 +168,21 @@ validate_slug() {
167168
}
168169

169170
###
170-
## gh_api <path_or_full_url> [extra curl args...]
171+
## gh_api <path_or_full_url> [--api-version VERSION] [extra curl args...]
171172
## GitHub REST/GraphQL API helper with Bearer auth and rate-limit retries.
172173
## Prepends API_URL_PREFIX when the first argument starts with /.
173174
## Returns __404__ / __422__ for those status codes rather than failing.
174-
## Any extra arguments after the URL are passed directly to curl (e.g. -X POST -d ...).
175+
## Pass --api-version VERSION immediately after the URL to override the default
176+
## API version (2022-11-28). Any remaining arguments are passed to curl.
175177
###
176178
gh_api() {
177179
local url="$1"
180+
local api_version="2022-11-28"
178181
shift
182+
if [[ "${1:-}" == "--api-version" ]]; then
183+
api_version="$2"
184+
shift 2
185+
fi
179186
[[ "${url}" == http* ]] || url="${API_URL_PREFIX}${url}"
180187

181188
local attempt
@@ -184,7 +191,7 @@ gh_api() {
184191
body=$(curl -s -w "\n%{http_code}" \
185192
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
186193
-H "Accept: application/vnd.github+json" \
187-
-H "X-GitHub-Api-Version: 2022-11-28" \
194+
-H "X-GitHub-Api-Version: ${api_version}" \
188195
"$@" "${url}")
189196
http_code=$(echo "${body}" | tail -1)
190197
body=$(echo "${body}" | sed '$d')

org-admin/github-add-repo-collaborators-by-pattern/github-add-repo-collaborators-by-pattern.sh

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -51,16 +51,6 @@ usage() {
5151
echo "Optional: PERMISSION=push REPO_EXCLUDE_REGEX=<regex> API_URL_PREFIX=<url>"
5252
}
5353

54-
get_repo_pagination() {
55-
local repo_pages
56-
repo_pages=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" -I "${API_URL_PREFIX}/orgs/${ORG}/repos?per_page=100" | grep -Eo '&page=[0-9]+' | grep -Eo '[0-9]+' | tail -1)
57-
echo "${repo_pages:-1}"
58-
}
59-
60-
limit_repo_pagination() {
61-
seq "$(get_repo_pagination)"
62-
}
63-
6454
validate() {
6555
require_env_var GITHUB_TOKEN "GitHub token"
6656
require_env_var ORG "GitHub organization"
@@ -79,7 +69,7 @@ validate() {
7969

8070
get_matching_repos() {
8171
local page repos_json
82-
for page in $(limit_repo_pagination); do
72+
for page in $(seq "$(get_repo_page_count "${API_URL_PREFIX}/orgs/${ORG}/repos?per_page=100")"); do
8373
repos_json=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" -H "Accept: application/json" "${API_URL_PREFIX}/orgs/${ORG}/repos?type=all&per_page=100&page=${page}&sort=full_name")
8474

8575
if [ -n "${REPO_EXCLUDE_REGEX}" ]; then

org-admin/github-add-repo-permissions/github-add-repo-permissions.sh

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,6 @@ if [ -n "${REPO_NAME_FILTER}" ]; then
7070
print_status "Repository filter: ${REPO_NAME_FILTER}*"
7171
fi
7272

73-
get_repo_pagination () {
74-
repo_pages=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" -I "${API_URL_PREFIX}/orgs/${ORG}/repos?per_page=100" | grep -Eo '&page=[0-9]+' | grep -Eo '[0-9]+' | tail -1;)
75-
echo "${repo_pages:-1}"
76-
}
77-
78-
limit_repo_pagination () {
79-
seq "$(get_repo_pagination)"
80-
}
81-
8273
apply_team_permissions () {
8374
local REPO_NAME=$1
8475
local PERMISSION=$2
@@ -107,7 +98,7 @@ process_repos () {
10798
local REPO
10899
local repos_json
109100

110-
for PAGE in $(limit_repo_pagination); do
101+
for PAGE in $(seq "$(get_repo_page_count "${API_URL_PREFIX}/orgs/${ORG}/repos?per_page=100")"); do
111102
repos_json=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" "${API_URL_PREFIX}/orgs/${ORG}/repos?page=${PAGE}&per_page=100&sort=full_name")
112103

113104
if ! echo "${repos_json}" | jq -e 'type == "array"' > /dev/null 2>&1; then

0 commit comments

Comments
 (0)