Module: StillActive::Workflow
Instance Method Summary collapse
Instance Method Details
#call(&on_progress) ⇒ Object
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/still_active/workflow.rb', line 34 def call(&on_progress) task = Async do # Load the optional ruby-advisory-db once, before the fan-out, so the # read-only Database is shared across fibers rather than reloaded per gem. advisory_db = RubyAdvisoryDb.load catalog = StillActive.config.alternatives ? CatalogIndex.load : nil # The Ruby support window (oldest-supported / latest-stable / EOL cycles) # is a per-run constant, so fetch it once here rather than per gem. nil when # the endoflife feed is unavailable -> the language-ceiling signal quietly # sits out, exactly like a missing advisory_db. This runs OUTSIDE the # per-gem rescue, so a defensive rescue keeps a best-effort enrichment from # ever aborting the whole audit (the "never crash the core" contract). ruby_range = begin RubyHelper.supported_ruby_range rescue StandardError => e $stderr.puts("warning: Ruby support window lookup failed: #{e.class} (#{e.}); skipping language-ceiling checks") nil end # Resolve the GitHub token once here, single-fibered, before the fan-out: # provider_for reads it per gem across fibers and gh_cli_token shells out, # so resolving eagerly keeps that off the concurrent path and guarantees # every fiber sees one consistent value (token -> live GitHub, incl. private # repos; only a genuinely absent token falls back to ecosyste.ms). StillActive.config.github_oauth_token = Async::Barrier.new semaphore = Async::Semaphore.new(StillActive.config.parallelism, parent: ) result_object = {} # Memoizes each capped dep's latest version across gems for the run, so a # dep pinned by several dormant gems is resolved once. Shared, single-writer # under Async's cooperative scheduling (a duplicate concurrent miss just # refetches the same value; it can't corrupt the map). constraint_cache = {} total = StillActive.config.gems.size completed = 0 StillActive.config.gems.each_with_object(result_object) do |gem, hash| semaphore.async do gem_info( gem_name: gem[:name], result_object: hash, gem_version: gem[:version], source_type: gem[:source_type] || :rubygems, source_uri: gem[:source_uri], direct: gem.fetch(:direct, true), dependency_path: gem[:dependency_path], advisory_db: advisory_db, catalog: catalog, constraint_cache: constraint_cache, ruby_range: ruby_range, ) rescue Octokit::TooManyRequests $stderr.print("\r\e[K") if on_progress $stderr.puts("rate limited checking #{gem[:name]}: set GITHUB_TOKEN to increase your limit") rescue StandardError => e $stderr.print("\r\e[K") if on_progress $stderr.puts("error occurred for #{gem[:name]}: #{e.class}\n\t#{e.}") ensure completed += 1 on_progress&.call(completed, total) end end .wait # Whole-tree correlation, once every gem's signals are in: a ceiling's # "upgrade to lift it" must not contradict a poison finding that caps the # same gem below that upgrade. CeilingReconciler.reconcile_ceiling_with_poison(result_object) # Flag poison caps that pin a vulnerable dependency: "a dormant package is # holding you on a known-vulnerable dep, below the fix." No extra fetches. PoisonSecurityCorrelator.correlate(result_object) # Gems are inserted as their async tasks finish, so the natural order is # nondeterministic completion order. Sort by name once here so every # consumer (JSON, SARIF, the baseline diff) gets a stable, diffable order. result_object.sort_by { |name, _| name }.to_h end task.wait end |
#ruby_freshness ⇒ Object
111 112 113 |
# File 'lib/still_active/workflow.rb', line 111 def ruby_freshness RubyHelper.ruby_freshness end |