Class: Utils::Analytics::FormulaFeeds Private
- Defined in:
- utils/analytics/formula_feeds.rb
Overview
This class is part of a private API. This class may only be used in the Homebrew/brew repository. Third parties should avoid using this class if possible, as it may be removed or changed without warning.
Collects bounded formula feeds from first-parent Git history.
Defined Under Namespace
Classes: Change
Constant Summary collapse
- Entry =
This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.
T.type_alias { T::Hash[String, T.any(String, T::Array[String])] }
- Builds =
This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.
T.type_alias { T::Hash[String, FormulaHistory::Build] }
- LIMITS =
This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.
T.let({ "new" => 50, "updated" => 500 }.freeze, T::Hash[String, Integer])
- BATCH_SIZE =
This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.
100
Instance Method Summary collapse
- #builds(path, blob, sources) ⇒ Builds private
- #changes(offset) ⇒ Array<Change>? private
- #generate ⇒ Hash{String => String, Array<Entry>} private
- #initialize(repository, revision: "HEAD", limits: LIMITS) ⇒ void constructor private
Constructor Details
#initialize(repository, revision: "HEAD", limits: LIMITS) ⇒ void
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
26 27 28 29 30 31 |
# File 'utils/analytics/formula_feeds.rb', line 26 def initialize(repository, revision: "HEAD", limits: LIMITS) @history = T.let(FormulaHistory.new(repository), FormulaHistory) @revision = T.let(@history.git("rev-parse", "--verify", "#{revision}^{commit}"), String) @limits = limits @builds = T.let({}, T::Hash[[String, String], Builds]) end |
Instance Method Details
#builds(path, blob, sources) ⇒ Builds
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
81 82 83 84 85 86 87 |
# File 'utils/analytics/formula_feeds.rb', line 81 def builds(path, blob, sources) @builds[[path, blob]] ||= @history.builds(path, sources.fetch(blob)).tap do |builds| if builds.values.any?(&:error) raise "Cannot read formula metadata for #{path} at #{blob}" end end end |
#changes(offset) ⇒ Array<Change>?
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'utils/analytics/formula_feeds.rb', line 90 def changes(offset) output = @history.git("log", @revision, "--first-parent", "--raw", "--root", "--no-abbrev", "--diff-merges=first-parent", "--find-renames=50%", "--max-count=#{BATCH_SIZE}", "--skip=#{offset}", "--format=%x00commit%x00%H%x00%ct", "-z") return if output.empty? tokens = output.split("\0") result = T.let([], T::Array[Change]) deleted = T.let({}, T::Hash[[String, String], [String, String]]) commit = T.let("", String) = 0 while (token = tokens.shift) if token == "commit" commit = tokens.shift || raise("Missing commit") = Integer(tokens.shift || raise("Missing commit date"), 10) elsif token.lstrip.start_with?(":") = token.lstrip.delete_prefix(":").split old_mode = .fetch(0) mode = .fetch(1) before = .fetch(2) after = .fetch(3) status = .fetch(4) old_path = tokens.shift || raise("Missing formula path") path = status.start_with?("R", "C") ? (tokens.shift || raise("Missing renamed formula path")) : old_path if status == "D" && %w[100644 100755].include?(old_mode) && @history.formula_path?(old_path) deleted[[commit, File.basename(old_path)]] = [old_path, before] end next if %w[100644 100755].exclude?(mode) || !@history.formula_path?(path) added = %w[100644 100755].exclude?(old_mode) || !@history.formula_path?(old_path) result << Change.new(commit:, timestamp:, path:, old_path:, before: added ? nil : before, after:, added:) end end result.each do |change| next unless change.added next unless (previous = deleted[[change.commit, File.basename(change.path)]]) change.old_path, change.before = previous change.added = false end result end |
#generate ⇒ Hash{String => String, Array<Entry>}
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
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 |
# File 'utils/analytics/formula_feeds.rb', line 34 def generate if @history.git("rev-parse", "--is-shallow-repository") == "true" raise "Formula feeds require complete Git history; unshallow the core checkout first" end feeds = @limits.to_h { |name, _limit| [name, T.let([], T::Array[Entry])] } offset = 0 until feeds.all? { |name, entries| entries.length >= @limits.fetch(name) } changes = changes(offset) break if changes.nil? selected = changes.select do |change| feed = change.added ? "new" : "updated" feeds.fetch(feed).length < @limits.fetch(feed) end blobs = selected.flat_map { |change| [change.before, change.after] }.compact.uniq sources = blobs.empty? ? {} : @history.sources(blobs) selected.each do |change| feed = change.added ? "new" : "updated" next if feeds.fetch(feed).length >= @limits.fetch(feed) after = builds(change.path, change.after, sources) before_blob = change.before before = before_blob ? builds(change.old_path, before_blob, sources) : nil next if before && after.none? do |platform, build| old = before.fetch(platform) old.version && build.version && (old.version != build.version || build.revision > old.revision) end feeds.fetch(feed) << { "id" => "#{change.commit}:#{change.path}", "name" => File.basename(change.path, ".rb"), "date" => Time.at(change.).utc.iso8601, "commit" => change.commit, "versions" => after.values.filter_map do |build| next unless (version = build.version) build.revision.positive? ? "#{version}_#{build.revision}" : version end.uniq, } end offset += BATCH_SIZE end { "revision" => @revision, **feeds } end |