Module: StillActive::Workflow
Instance Method Summary collapse
Instance Method Details
#call(&on_progress) ⇒ Object
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 110 111 |
# File 'lib/still_active/workflow.rb', line 36 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 => e warn("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 warn("rate limited checking #{gem[:name]}: set GITHUB_TOKEN to increase your limit") rescue => e $stderr.print("\r\e[K") if on_progress warn("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
113 114 115 |
# File 'lib/still_active/workflow.rb', line 113 def ruby_freshness RubyHelper.ruby_freshness end |