From 8d4bdbafc46a8b66bbd1372dc990ce30eb7d19b1 Mon Sep 17 00:00:00 2001 From: Issy Long Date: Thu, 23 Feb 2023 23:27:38 +0000 Subject: [PATCH 01/10] dev-cmd/contributions: CSV output of totals per maintainer - Turns out in my head a few days ago I was overcomplicating this. I had a brainwave while in the shower. - Some refactoring so that we call `totals` to sum up the hash of hashes less, since the grand total numbers are now used in multiple places. --- Library/Homebrew/dev-cmd/contributions.rb | 33 ++++++++++++++++------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index 035c16a2f7..1648698e02 100755 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -39,7 +39,7 @@ module Homebrew description: "A GitHub username or email address of a specific person to find contribution data for." switch "--csv", - description: "Print a CSV of a user's contributions across repositories over the time period." + description: "Print a CSV of contributions across repositories over the time period." end end @@ -48,6 +48,7 @@ module Homebrew args = contributions_args.parse results = {} + grand_totals = {} all_repos = args.repositories.nil? || args.repositories.include?("all") repos = if all_repos @@ -61,13 +62,13 @@ module Homebrew if args.user user = args.user results[user] = scan_repositories(repos, user, args) - puts "#{user} contributed #{total(results[user])} times #{time_period(args)}." - puts generate_csv(T.must(user), results[user]) if args.csv? + grand_totals[user] = total(results[user]) + + puts "#{user} contributed #{grand_totals[user]} times #{time_period(args)}." + puts generate_csv(T.must(user), results[user], grand_totals[user]) if args.csv? return end - odie "CSVs not yet supported for the full list of maintainers at once." if args.csv? - maintainers = GitHub.members_by_team("Homebrew", "maintainers") maintainers.each do |username, _| # TODO: Using the GitHub username to scan the `git log` undercounts some @@ -76,8 +77,12 @@ module Homebrew # TODO: Switch to using the GitHub APIs instead of `git log` if # they ever support trailers. results[username] = scan_repositories(repos, username, args) - puts "#{username} contributed #{total(results[username])} times #{time_period(args)}." + grand_totals[username] = total(results[username]) + + puts "#{username} contributed #{grand_totals[username]} times #{time_period(args)}." end + + puts generate_maintainers_csv(grand_totals) if args.csv? end sig { params(repo: String).returns(Pathname) } @@ -100,8 +105,18 @@ module Homebrew end end - sig { params(user: String, results: Hash).returns(String) } - def generate_csv(user, results) + sig { params(totals: Hash).returns(String) } + def generate_maintainers_csv(totals) + CSV.generate do |csv| + csv << %w[user total] + totals.each do |user, total| + csv << [user, total] + end + end + end + + sig { params(user: String, results: Hash, grand_total: Integer).returns(String) } + def generate_csv(user, results, grand_total) CSV.generate do |csv| csv << %w[user repo commits coauthorships signoffs total] results.each do |repo, counts| @@ -114,7 +129,7 @@ module Homebrew counts.values.sum, ] end - csv << [user, "*", "*", "*", "*", total(results)] + csv << [user, "*", "*", "*", "*", grand_total] end end From e68379f5dfc5f7878a55a06d9c20ec7e7caa4210 Mon Sep 17 00:00:00 2001 From: Issy Long Date: Sat, 25 Feb 2023 00:29:37 +0000 Subject: [PATCH 02/10] dev-cmd/contributions: Fill in the "total" CSV row - Using the new "total across commits/coauthors/signoffs" data per-user, this also generates a more complete maintainers CSV. --- Library/Homebrew/dev-cmd/contributions.rb | 41 +++++++++++++++++------ 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index 1648698e02..868e10f43b 100755 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -64,7 +64,7 @@ module Homebrew results[user] = scan_repositories(repos, user, args) grand_totals[user] = total(results[user]) - puts "#{user} contributed #{grand_totals[user]} times #{time_period(args)}." + puts "#{user} contributed #{grand_totals[user].values.sum} times #{time_period(args)}." puts generate_csv(T.must(user), results[user], grand_totals[user]) if args.csv? return end @@ -79,7 +79,7 @@ module Homebrew results[username] = scan_repositories(repos, username, args) grand_totals[username] = total(results[username]) - puts "#{username} contributed #{grand_totals[username]} times #{time_period(args)}." + puts "#{username} contributed #{grand_totals[username].values.sum} times #{time_period(args)}." end puts generate_maintainers_csv(grand_totals) if args.csv? @@ -108,14 +108,15 @@ module Homebrew sig { params(totals: Hash).returns(String) } def generate_maintainers_csv(totals) CSV.generate do |csv| - csv << %w[user total] + csv << %w[user repo commits coauthorships signoffs total] + totals.each do |user, total| - csv << [user, total] + csv << grand_total_row(user, total) end end end - sig { params(user: String, results: Hash, grand_total: Integer).returns(String) } + sig { params(user: String, results: Hash, grand_total: Hash).returns(String) } def generate_csv(user, results, grand_total) CSV.generate do |csv| csv << %w[user repo commits coauthorships signoffs total] @@ -129,10 +130,22 @@ module Homebrew counts.values.sum, ] end - csv << [user, "*", "*", "*", "*", grand_total] + csv << grand_total_row(user, grand_total) end end + sig { params(user: String, grand_total: Hash).returns(Array) } + def grand_total_row(user, grand_total) + [ + user, + "all", + grand_total[:commits], + grand_total[:coauthorships], + grand_total[:signoffs], + grand_total.values.sum, + ] + end + def scan_repositories(repos, person, args) data = {} @@ -166,12 +179,18 @@ module Homebrew data end - sig { params(results: Hash).returns(Integer) } + sig { params(results: Hash).returns(Hash) } def total(results) - results - .values # [{:commits=>1, :coauthorships=>0, :signoffs=>3}, {:commits=>500, :coauthorships=>2, :signoffs=>450}] - .map(&:values) # [[1, 0, 3], [500, 2, 450]] - .sum(&:sum) # 956 + totals = { commits: 0, coauthorships: 0, signoffs: 0 } + + # {"brew"=>{:commits=>9,:coauthorships=>6,:signoffs=>3},"core"=>{:commits=>15,:coauthorships=>10,:signoffs=>5}} + results.each_value do |counts| + counts.each do |kind, count| + totals[kind] += count + end + end + + totals # {:commits=>24,:coauthorships=>16,signoffs=>8} end sig { params(repo_path: Pathname, person: String, trailer: String, args: Homebrew::CLI::Args).returns(Integer) } From 5d406ae54ab3d8ea0887ea6252c4092d72d471f1 Mon Sep 17 00:00:00 2001 From: BrewTestBot <1589480+BrewTestBot@users.noreply.github.com> Date: Sat, 25 Feb 2023 17:52:54 +0000 Subject: [PATCH 03/10] Update manpage and completions. Autogenerated by the [sponsors-maintainers-man-completions](https://github.com/Homebrew/brew/blob/HEAD/.github/workflows/sponsors-maintainers-man-completions.yml) workflow. --- completions/fish/brew.fish | 2 +- completions/zsh/_brew | 2 +- docs/Manpage.md | 2 +- manpages/brew.1 | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/completions/fish/brew.fish b/completions/fish/brew.fish index 1fe3aa4422..9d88c13fab 100644 --- a/completions/fish/brew.fish +++ b/completions/fish/brew.fish @@ -537,7 +537,7 @@ __fish_brew_complete_arg 'config' -l verbose -d 'Make some output more verbose' __fish_brew_complete_cmd 'contributions' 'Contributions to Homebrew repos' -__fish_brew_complete_arg 'contributions' -l csv -d 'Print a CSV of a user\'s contributions across repositories over the time period' +__fish_brew_complete_arg 'contributions' -l csv -d 'Print a CSV of contributions across repositories over the time period' __fish_brew_complete_arg 'contributions' -l debug -d 'Display any debugging information' __fish_brew_complete_arg 'contributions' -l from -d 'Date (ISO-8601 format) to start searching contributions' __fish_brew_complete_arg 'contributions' -l help -d 'Show this message' diff --git a/completions/zsh/_brew b/completions/zsh/_brew index b4406ef039..8e5fb74f7e 100644 --- a/completions/zsh/_brew +++ b/completions/zsh/_brew @@ -666,7 +666,7 @@ _brew_config() { # brew contributions _brew_contributions() { _arguments \ - '--csv[Print a CSV of a user'\''s contributions across repositories over the time period]' \ + '--csv[Print a CSV of contributions across repositories over the time period]' \ '--debug[Display any debugging information]' \ '--from[Date (ISO-8601 format) to start searching contributions]' \ '--help[Show this message]' \ diff --git a/docs/Manpage.md b/docs/Manpage.md index c360f2b0b3..35bfa5bb30 100644 --- a/docs/Manpage.md +++ b/docs/Manpage.md @@ -1124,7 +1124,7 @@ Contributions to Homebrew repos. * `--user`: A GitHub username or email address of a specific person to find contribution data for. * `--csv`: - Print a CSV of a user's contributions across repositories over the time period. + Print a CSV of contributions across repositories over the time period. ### `create` [*`options`*] *`URL`* diff --git a/manpages/brew.1 b/manpages/brew.1 index f9722eccfc..5b0e63eb9c 100644 --- a/manpages/brew.1 +++ b/manpages/brew.1 @@ -1619,7 +1619,7 @@ A GitHub username or email address of a specific person to find contribution dat . .TP \fB\-\-csv\fR -Print a CSV of a user\'s contributions across repositories over the time period\. +Print a CSV of contributions across repositories over the time period\. . .SS "\fBcreate\fR [\fIoptions\fR] \fIURL\fR" Generate a formula or, with \fB\-\-cask\fR, a cask for the downloadable file at \fIURL\fR and open it in the editor\. Homebrew will attempt to automatically derive the formula name and version, but if it fails, you\'ll have to make your own template\. The \fBwget\fR formula serves as a simple example\. For the complete API, see: \fIhttps://rubydoc\.brew\.sh/Formula\fR From 7b8f1c871408dc853856fb67c732f5f5b09d2714 Mon Sep 17 00:00:00 2001 From: Issy Long Date: Sat, 25 Feb 2023 18:04:01 +0000 Subject: [PATCH 04/10] dev-cmd/contributions: Order the CSV by highest contributions total --- Library/Homebrew/dev-cmd/contributions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index 868e10f43b..f20b0dd3f4 100755 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -110,7 +110,7 @@ module Homebrew CSV.generate do |csv| csv << %w[user repo commits coauthorships signoffs total] - totals.each do |user, total| + totals.sort_by { |_, v| -v.values.sum }.each do |user, total| csv << grand_total_row(user, total) end end From 8c75eab88a9d3bdbe6db2723d1efd91cbc0a11aa Mon Sep 17 00:00:00 2001 From: Issy Long Date: Sat, 25 Feb 2023 19:10:17 +0000 Subject: [PATCH 05/10] dev-cmd/contributions: Count PR reviews since they're super important - The search APIs don't have that high a rate limit but we shouldn't need to worry about that too much because, to get counts, the JSON response comes with a `total_count` number. --- Library/Homebrew/dev-cmd/contributions.rb | 16 +++++++++++----- Library/Homebrew/utils/github.rb | 20 +++++++++++++++++--- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index 868e10f43b..0e080b6837 100755 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -108,7 +108,7 @@ module Homebrew sig { params(totals: Hash).returns(String) } def generate_maintainers_csv(totals) CSV.generate do |csv| - csv << %w[user repo commits coauthorships signoffs total] + csv << %w[user repo commits coauthorships signoffs reviews total] totals.each do |user, total| csv << grand_total_row(user, total) @@ -119,7 +119,7 @@ module Homebrew sig { params(user: String, results: Hash, grand_total: Hash).returns(String) } def generate_csv(user, results, grand_total) CSV.generate do |csv| - csv << %w[user repo commits coauthorships signoffs total] + csv << %w[user repo commits coauthorships signoffs reviews total] results.each do |repo, counts| csv << [ user, @@ -127,6 +127,7 @@ module Homebrew counts[:commits], counts[:coauthorships], counts[:signoffs], + counts[:reviews], counts.values.sum, ] end @@ -142,6 +143,7 @@ module Homebrew grand_total[:commits], grand_total[:coauthorships], grand_total[:signoffs], + grand_total[:reviews], grand_total.values.sum, ] end @@ -173,6 +175,7 @@ module Homebrew commits: GitHub.repo_commit_count_for_user(repo_full_name, person, args), coauthorships: git_log_trailers_cmd(T.must(repo_path), person, "Co-authored-by", args), signoffs: git_log_trailers_cmd(T.must(repo_path), person, "Signed-off-by", args), + reviews: GitHub.count_issues("", is: "pr", repo: repo_full_name, reviewed_by: person), } end @@ -181,16 +184,19 @@ module Homebrew sig { params(results: Hash).returns(Hash) } def total(results) - totals = { commits: 0, coauthorships: 0, signoffs: 0 } + totals = { commits: 0, coauthorships: 0, signoffs: 0, reviews: 0 } - # {"brew"=>{:commits=>9,:coauthorships=>6,:signoffs=>3},"core"=>{:commits=>15,:coauthorships=>10,:signoffs=>5}} + # { + # "brew"=>{:commits=>9,:coauthorships=>6,:signoffs=>3,:reviews=>1}, + # "core"=>{:commits=>15,:coauthorships=>10,:signoffs=>5,:reviews=>2} + # } results.each_value do |counts| counts.each do |kind, count| totals[kind] += count end end - totals # {:commits=>24,:coauthorships=>16,signoffs=>8} + totals # {:commits=>24,:coauthorships=>16,:signoffs=>8,:reviews=>3} end sig { params(repo_path: Pathname, person: String, trailer: String, args: Homebrew::CLI::Args).returns(Integer) } diff --git a/Library/Homebrew/utils/github.rb b/Library/Homebrew/utils/github.rb index 1ca085109b..691bf4955e 100644 --- a/Library/Homebrew/utils/github.rb +++ b/Library/Homebrew/utils/github.rb @@ -37,7 +37,11 @@ module GitHub end def search_issues(query, **qualifiers) - search("issues", query, **qualifiers) + search_results_items("issues", query, **qualifiers) + end + + def count_issues(query, **qualifiers) + search_results_count("issues", query, **qualifiers) end def create_gist(files, description, private:) @@ -163,7 +167,7 @@ module GitHub params = main_params params += qualifiers.flat_map do |key, value| - Array(value).map { |v| "#{key}:#{v}" } + Array(value).map { |v| "#{key.to_s.tr("_", "-")}:#{v}" } end "q=#{URI.encode_www_form_component(params.join(" "))}&per_page=100" @@ -176,7 +180,17 @@ module GitHub def search(entity, *queries, **qualifiers) uri = url_to "search", entity uri.query = search_query_string(*queries, **qualifiers) - API.open_rest(uri) { |json| json.fetch("items", []) } + API.open_rest(uri) + end + + def search_results_items(entity, *queries, **qualifiers) + json = search(entity, *queries, **qualifiers) + json.fetch("items", []) + end + + def search_results_count(entity, *queries, **qualifiers) + json = search(entity, *queries, **qualifiers) + json.fetch("total_count", 0) end def approved_reviews(user, repo, pr, commit: nil) From 29bb0ee3bf622c95fef9cf3c7e8fe86e6184bc17 Mon Sep 17 00:00:00 2001 From: Rylan Polster Date: Sat, 25 Feb 2023 16:24:58 -0500 Subject: [PATCH 06/10] Set tap for casks when loading from contents via API --- Library/Homebrew/cask/cask_loader.rb | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Library/Homebrew/cask/cask_loader.rb b/Library/Homebrew/cask/cask_loader.rb index 0473882f5f..7f046eff22 100644 --- a/Library/Homebrew/cask/cask_loader.rb +++ b/Library/Homebrew/cask/cask_loader.rb @@ -14,7 +14,7 @@ module Cask # Loads a cask from a string. class FromContentLoader - attr_reader :content + attr_reader :content, :tap def self.can_load?(ref) return false unless ref.respond_to?(:to_str) @@ -32,8 +32,9 @@ module Cask content.match?(@regex) end - def initialize(content) + def initialize(content, tap: nil) @content = content.force_encoding("UTF-8") + @tap = tap end def load(config:) @@ -48,7 +49,8 @@ module Cask checksum = { "sha256" => Digest::SHA256.hexdigest(content), } - Cask.new(header_token, source: content, source_checksum: checksum, **options, config: @config, &block) + Cask.new(header_token, source: content, source_checksum: checksum, tap: tap, **options, + config: @config, &block) end end @@ -234,6 +236,7 @@ module Cask cask_source = JSON.pretty_generate(json_cask) json_cask = Homebrew::API.merge_variations(json_cask).deep_symbolize_keys + tap = Tap.fetch(json_cask[:tap]) if json_cask[:tap].to_s.include?("/") # Use the cask-source API if there are any `*flight` blocks or the cask has multiple languages if json_cask[:artifacts].any? { |artifact| FLIGHT_STANZAS.include?(artifact.keys.first) } || @@ -241,11 +244,9 @@ module Cask cask_source = Homebrew::API::Cask.fetch_source(token, git_head: json_cask[:tap_git_head], sha256: json_cask.dig(:ruby_source_checksum, :sha256)) - return FromContentLoader.new(cask_source).load(config: config) + return FromContentLoader.new(cask_source, tap: tap).load(config: config) end - tap = Tap.fetch(json_cask[:tap]) if json_cask[:tap].to_s.include?("/") - user_agent = json_cask.dig(:url_specs, :user_agent) json_cask[:url_specs][:user_agent] = user_agent[1..].to_sym if user_agent && user_agent[0] == ":" if (using = json_cask.dig(:url_specs, :using)) From 1edb59e08607998452d60f5d16fdaa30655bd107 Mon Sep 17 00:00:00 2001 From: Issy Long Date: Sat, 25 Feb 2023 21:54:17 +0000 Subject: [PATCH 07/10] test/search: Fix stubbing of `GitHub::API.open_rest` call Test failure: ``` Failure/Error: expect(described_class.search_taps("some-formula")) .to match(formulae: ["homebrew/foo/some-formula"], casks: ["homebrew/bar/some-cask"]) GitHub::API asked to yield |[{"items"=>[{"path"=>"Formula/some-formula.rb", "repository"=>{"full_name"=>"Homebrew/homebrew-foo"}}, {"path"=>"Casks/some-cask.rb", "repository"=>{"full_name"=>"Homebrew/homebrew-bar"}}]}]| but no block was passed ``` --- Library/Homebrew/test/search_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Library/Homebrew/test/search_spec.rb b/Library/Homebrew/test/search_spec.rb index ef3f383919..6a37c77183 100644 --- a/Library/Homebrew/test/search_spec.rb +++ b/Library/Homebrew/test/search_spec.rb @@ -39,7 +39,7 @@ describe Homebrew::Search do ], } - allow(GitHub::API).to receive(:open_rest).and_yield(json_response) + allow(GitHub::API).to receive(:open_rest).and_return(json_response) expect(described_class.search_taps("some-formula")) .to match(formulae: ["homebrew/foo/some-formula"], casks: ["homebrew/bar/some-cask"]) From 99d2bb9a53745afd3b4c586dcc6afc4038e2dd4f Mon Sep 17 00:00:00 2001 From: Issy Long Date: Sat, 25 Feb 2023 21:57:50 +0000 Subject: [PATCH 08/10] utils/github: Use `search_results_items` method for code search --- Library/Homebrew/utils/github.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Library/Homebrew/utils/github.rb b/Library/Homebrew/utils/github.rb index 691bf4955e..a14f1c8850 100644 --- a/Library/Homebrew/utils/github.rb +++ b/Library/Homebrew/utils/github.rb @@ -61,7 +61,7 @@ module GitHub end def search_code(repo: nil, user: "Homebrew", path: ["Formula", "Casks", "."], filename: nil, extension: "rb") - matches = search("code", user: user, path: path, filename: filename, extension: extension, repo: repo) + matches = search_results_items("code", user: user, path: path, filename: filename, extension: extension, repo: repo) return matches if matches.blank? matches.map do |match| From 591afa8ee00d7303d3e248e60b7e8ff2e219460b Mon Sep 17 00:00:00 2001 From: Issy Long Date: Sat, 25 Feb 2023 22:27:48 +0000 Subject: [PATCH 09/10] utils/github: Appease `brew style` line length --- Library/Homebrew/utils/github.rb | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Library/Homebrew/utils/github.rb b/Library/Homebrew/utils/github.rb index a14f1c8850..3137393e6d 100644 --- a/Library/Homebrew/utils/github.rb +++ b/Library/Homebrew/utils/github.rb @@ -61,7 +61,14 @@ module GitHub end def search_code(repo: nil, user: "Homebrew", path: ["Formula", "Casks", "."], filename: nil, extension: "rb") - matches = search_results_items("code", user: user, path: path, filename: filename, extension: extension, repo: repo) + matches = search_results_items( + "code", + user: user, + path: path, + filename: filename, + extension: extension, + repo: repo, + ) return matches if matches.blank? matches.map do |match| From 85cebc5c8f79f9b3baa76b40a61cadfcf549f92c Mon Sep 17 00:00:00 2001 From: BrewTestBot <1589480+BrewTestBot@users.noreply.github.com> Date: Sun, 26 Feb 2023 00:32:18 +0000 Subject: [PATCH 10/10] sorbet: Update RBI files. Autogenerated by the [sorbet](https://github.com/Homebrew/brew/blob/master/.github/workflows/sorbet.yml) workflow. --- Library/Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Library/Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi b/Library/Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi index 1fcb39ee96..2416582555 100644 --- a/Library/Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi +++ b/Library/Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi @@ -5339,6 +5339,9 @@ class Object HOMEBREW_LOGS = ::T.let(nil, ::T.untyped) HOMEBREW_MACOS_ARM_DEFAULT_PREFIX = ::T.let(nil, ::T.untyped) HOMEBREW_MACOS_ARM_DEFAULT_REPOSITORY = ::T.let(nil, ::T.untyped) + HOMEBREW_MACOS_NEWEST_UNSUPPORTED = ::T.let(nil, ::T.untyped) + HOMEBREW_MACOS_OLDEST_ALLOWED = ::T.let(nil, ::T.untyped) + HOMEBREW_MACOS_OLDEST_SUPPORTED = ::T.let(nil, ::T.untyped) HOMEBREW_MAIN_TAP_CASK_REGEX = ::T.let(nil, ::T.untyped) HOMEBREW_OFFICIAL_REPO_PREFIXES_REGEX = ::T.let(nil, ::T.untyped) HOMEBREW_PHYSICAL_PROCESSOR = ::T.let(nil, ::T.untyped)