@@ -270,6 +270,89 @@ def add_message(type, file, line_number, message)
270270 client . messages << "::#{ type } file=#{ file } ,line=#{ line_number } ::#{ message } "
271271end
272272
273+ CACHE_FILE = File . expand_path ( "../.api-cache.json" , __dir__ )
274+ CACHE_TTL_SECONDS = 24 * 60 * 60 # 24 hours
275+
276+ def load_api_cache!
277+ return unless File . exist? ( CACHE_FILE )
278+
279+ data = JSON . parse ( File . read ( CACHE_FILE ) )
280+ now = Time . now . to_i
281+ ttl = CACHE_TTL_SECONDS
282+
283+ if data [ "repos" ]
284+ data [ "repos" ] . each do |key , entry |
285+ cached_at = entry [ "cached_at" ]
286+ next unless cached_at
287+ next if now - cached_at . to_i > ttl
288+
289+ result = entry [ "value" ]
290+ # Reconstruct a minimal object that responds to .full_name
291+ cached = if result . nil?
292+ nil
293+ else
294+ next unless result [ "full_name" ]
295+
296+ Struct . new ( :full_name ) . new ( result [ "full_name" ] )
297+ end
298+ NewOctokit . class_variable_get ( :@@repos ) [ key ] = cached
299+ end
300+ end
301+
302+ if data [ "users" ]
303+ data [ "users" ] . each do |key , entry |
304+ cached_at = entry [ "cached_at" ]
305+ next unless cached_at
306+ next if now - cached_at . to_i > ttl
307+
308+ result = entry [ "value" ]
309+ cached = if result . nil?
310+ nil
311+ else
312+ next unless result [ "login" ]
313+
314+ Struct . new ( :login ) . new ( result [ "login" ] )
315+ end
316+ NewOctokit . class_variable_get ( :@@users ) [ key ] = cached
317+ end
318+ end
319+ rescue JSON ::ParserError , StandardError => e
320+ warn "Failed to load API cache: #{ e . message } "
321+ end
322+
323+ def save_api_cache!
324+ now = Time . now . to_i
325+ repos_data = { }
326+ users_data = { }
327+
328+ NewOctokit . class_variable_get ( :@@repos ) . each do |key , value |
329+ next if key == :skip_requests
330+ next if value == true
331+
332+ repos_data [ key . to_s ] = {
333+ "cached_at" => now ,
334+ "value" => value . nil? ? nil : { "full_name" => value . respond_to? ( :full_name ) ? value . full_name : value . to_s } ,
335+ }
336+ end
337+
338+ NewOctokit . class_variable_get ( :@@users ) . each do |key , value |
339+ next if key == :skip_requests
340+ next if value == true
341+
342+ users_data [ key . to_s ] = {
343+ "cached_at" => now ,
344+ "value" => value . nil? ? nil : { "login" => value . respond_to? ( :login ) ? value . login : value . to_s } ,
345+ }
346+ end
347+
348+ File . write ( CACHE_FILE , JSON . pretty_generate ( { "repos" => repos_data , "users" => users_data } ) )
349+ rescue StandardError => e
350+ warn "Failed to save API cache: #{ e . message } "
351+ end
352+
353+ # Load cached API results at startup
354+ load_api_cache!
355+
273356Minitest . after_run do
274357 warn "Repo checks were rate limited during this CI run" if NewOctokit . repos_skipped?
275358 warn "User checks were rate limited during this CI run" if NewOctokit . users_skipped?
@@ -279,4 +362,7 @@ def add_message(type, file, line_number, message)
279362 NewOctokit . messages . each do |message |
280363 puts message
281364 end
365+
366+ # Persist cache for next CI run
367+ save_api_cache!
282368end
0 commit comments