@@ -80,52 +80,6 @@ check_prerequisites() {
8080 print_success " Prerequisites OK"
8181}
8282
83- # ##
84- # # GitHub API helper – handles rate-limit retries
85- # # Usage: gh_api <path_or_url> [extra curl args...]
86- # ##
87- gh_api () {
88- local url=" $1 "
89- shift
90- # Prepend base URL if path starts with /
91- [[ " ${url} " == http* ]] || url=" ${API_URL_PREFIX}${url} "
92-
93- local attempt
94- for attempt in 1 2 3 4 5; do
95- local http_code
96- local body
97- body=$( curl -s -w " \n%{http_code}" \
98- -H " Authorization: Bearer ${GITHUB_TOKEN} " \
99- -H " Accept: application/vnd.github+json" \
100- -H " X-GitHub-Api-Version: 2022-11-28" \
101- " $@ " " ${url} " )
102- http_code=$( echo " ${body} " | tail -1)
103- body=$( echo " ${body} " | head -n -1)
104-
105- if [[ " ${http_code} " == " 200" ]]; then
106- echo " ${body} "
107- return 0
108- elif [[ " ${http_code} " == " 403" || " ${http_code} " == " 429" ]]; then
109- local retry_after=60
110- print_warning " Rate limited (HTTP ${http_code} ). Sleeping ${retry_after} s before retry ${attempt} /5..."
111- sleep " ${retry_after} "
112- elif [[ " ${http_code} " == " 404" ]]; then
113- echo " __404__"
114- return 0
115- elif [[ " ${http_code} " == " 422" ]]; then
116- # Code search not available for this org
117- echo " __422__"
118- return 0
119- else
120- print_warning " HTTP ${http_code} for ${url} (attempt ${attempt} /5)"
121- sleep 5
122- fi
123- done
124-
125- print_error " Failed to GET ${url} after 5 attempts"
126- return 1
127- }
128-
12983# ##
13084# # gh_api_link – like gh_api but also returns the Link header for pagination
13185# # Writes body to stdout, sets global RESPONSE_LINK
@@ -168,140 +122,6 @@ gh_api_link() {
168122 return 1
169123}
170124
171- # ##
172- # # Validate token
173- # ##
174- validate_token () {
175- print_status " Validating GitHub token..."
176- local resp
177- resp=$( gh_api " /user" )
178- local login
179- login=$( echo " ${resp} " | jq -r ' .login // empty' )
180- if [ -z " ${login} " ]; then
181- print_error " GITHUB_TOKEN is invalid or lacks required scopes."
182- exit 1
183- fi
184- print_success " Token validated."
185- }
186-
187- # ##
188- # # _paginate_orgs_endpoint <jq_filter> <url_template_with_PAGE_placeholder>
189- # # Internal helper: paginates an orgs list endpoint, prints one login per line.
190- # ##
191- _paginate_orgs_endpoint () {
192- local jq_filter=" $1 "
193- local url_template=" $2 " # must contain the literal string PAGE
194- local page=1
195- while true ; do
196- local url resp orgs_on_page count
197- url=" ${url_template/ PAGE/ ${page} } "
198- resp=$( gh_api " ${url} " )
199- if [[ " ${resp} " == " __404__" || " ${resp} " == " __422__" || -z " ${resp} " ]]; then
200- break
201- fi
202- orgs_on_page=$( echo " ${resp} " | jq -r " ${jq_filter} " 2> /dev/null || true)
203- if [ -z " ${orgs_on_page} " ]; then
204- break
205- fi
206- echo " ${orgs_on_page} "
207- count=$( echo " ${orgs_on_page} " | wc -l)
208- if [ " ${count} " -lt 100 ]; then
209- break
210- fi
211- page=$(( page + 1 ))
212- done
213- }
214-
215- # ##
216- # # _graphql_enterprise_orgs
217- # # Queries the GraphQL API for enterprise orgs (works for enterprise members,
218- # # not just owners). Prints one org login per line.
219- # ##
220- _graphql_enterprise_orgs () {
221- local cursor=" null"
222- while true ; do
223- local query
224- query=$( printf ' { "query": "{ enterprise(slug: \\"%s\\") { organizations(first: 100, after: %s) { nodes { login } pageInfo { hasNextPage endCursor } } } }" }' \
225- " ${ENTERPRISE} " " ${cursor} " )
226-
227- local resp
228- resp=$( curl -s -w " \n%{http_code}" \
229- -X POST \
230- -H " Authorization: Bearer ${GITHUB_TOKEN} " \
231- -H " Content-Type: application/json" \
232- -H " X-GitHub-Api-Version: 2022-11-28" \
233- " ${API_URL_PREFIX} /graphql" \
234- -d " ${query} " )
235-
236- local http_code body
237- http_code=$( echo " ${resp} " | tail -1)
238- body=$( echo " ${resp} " | head -n -1)
239-
240- if [[ " ${http_code} " != " 200" ]]; then
241- return 1
242- fi
243-
244- # Surface any GraphQL-level errors (e.g. token lacks enterprise access)
245- local gql_errors
246- gql_errors=$( echo " ${body} " | jq -r ' .errors // empty' 2> /dev/null || true)
247- if [ -n " ${gql_errors} " ]; then
248- return 1
249- fi
250-
251- echo " ${body} " | jq -r ' .data.enterprise.organizations.nodes[].login' 2> /dev/null || true
252-
253- local has_next end_cursor
254- has_next=$( echo " ${body} " | jq -r ' .data.enterprise.organizations.pageInfo.hasNextPage' )
255- end_cursor=$( echo " ${body} " | jq -r ' .data.enterprise.organizations.pageInfo.endCursor' )
256-
257- if [[ " ${has_next} " != " true" ]]; then
258- break
259- fi
260- cursor=" \" ${end_cursor} \" "
261- done
262- }
263-
264- # ##
265- # # get_enterprise_orgs
266- # # Prints one org login per line.
267- # # Strategy:
268- # # 1. Try /enterprises/{slug}/organizations REST endpoint (enterprise-owner token)
269- # # 2. Try GraphQL enterprise(slug).organizations (enterprise member token)
270- # # 3. Fall back to /user/orgs (any read:org token)
271- # ##
272- get_enterprise_orgs () {
273- print_status " Fetching organisations for enterprise '${ENTERPRISE} '..."
274-
275- # -- Attempt 1: REST enterprise endpoint --------------------------------
276- local probe
277- probe=$( gh_api " /enterprises/${ENTERPRISE} /organizations?per_page=1&page=1" )
278- if [[ " ${probe} " != " __404__" && " ${probe} " != " __422__" && -n " ${probe} " ]]; then
279- print_status " Using REST enterprise API endpoint."
280- _paginate_orgs_endpoint \
281- ' .organizations[].login' \
282- " /enterprises/${ENTERPRISE} /organizations?per_page=100&page=PAGE"
283- return 0
284- fi
285-
286- # -- Attempt 2: GraphQL enterprise query --------------------------------
287- print_warning " REST enterprise endpoint unavailable — trying GraphQL enterprise query..."
288- local gql_orgs
289- gql_orgs=$( _graphql_enterprise_orgs 2> /dev/null || true)
290- if [ -n " ${gql_orgs} " ]; then
291- print_status " Using GraphQL enterprise endpoint."
292- echo " ${gql_orgs} "
293- return 0
294- fi
295-
296- # -- Attempt 3: /user/orgs fallback -------------------------------------
297- print_warning " GraphQL enterprise query unavailable — falling back to /user/orgs."
298- print_warning " Set ORG_FILTER env var to restrict results to enterprise orgs only."
299- print_status " Example: export ORG_FILTER='^my-enterprise-prefix'"
300- _paginate_orgs_endpoint \
301- ' .[].login' \
302- " /user/orgs?per_page=100&page=PAGE"
303- }
304-
305125# ##
306126# # search_dockerfiles_in_org <org>
307127# # Appends TSV rows (org TAB repo_full_name TAB path TAB html_url) to REFS_TEMP
@@ -564,7 +384,7 @@ build_text_summary() {
564384# ##
565385main () {
566386 check_prerequisites
567- validate_token
387+ validate_github_token
568388
569389 mkdir -p " ${REPORT_DIR} "
570390
0 commit comments