diff --git a/Library/Homebrew/cli/args.rbi b/Library/Homebrew/cli/args.rbi index 32b1cd8e6e..e0aaee87f1 100644 --- a/Library/Homebrew/cli/args.rbi +++ b/Library/Homebrew/cli/args.rbi @@ -300,6 +300,15 @@ module Homebrew sig { returns(T.nilable(String)) } def screen_saverdir; end + sig { returns(T::Array[String])} + def repositories; end + + sig { returns(T.nilable(String)) } + def from; end + + sig { returns(T.nilable(String)) } + def to; end + sig { returns(T.nilable(T::Array[String])) } def groups; end diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb new file mode 100755 index 0000000000..946c40b8f1 --- /dev/null +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -0,0 +1,109 @@ +# typed: true +# frozen_string_literal: true + +require "cli/parser" + +module Homebrew + extend T::Sig + + module_function + + SUPPORTED_REPOS = [ + %w[brew core cask], + OFFICIAL_CMD_TAPS.keys.map { |t| t.delete_prefix("homebrew/") }, + OFFICIAL_CASK_TAPS.reject { |t| t == "cask" }, + ].flatten.freeze + + sig { returns(CLI::Parser) } + def contributions_args + Homebrew::CLI::Parser.new do + usage_banner "`contributions` [<--repositories>`=`]" + description <<~EOS + Contributions to Homebrew repos for a user. + + The first argument is a name (e.g. "BrewTestBot") or an email address (e.g. "brewtestbot@brew.sh"). + EOS + + comma_array "--repositories", + description: "Specify a comma-separated (no spaces) list of repositories to search. " \ + "Supported repositories: #{SUPPORTED_REPOS.map { |t| "`#{t}`" }.to_sentence}." \ + "Omitting this flag, or specifying `--repositories=all`, will search all repositories." + flag "--from=", + description: "Date (ISO-8601 format) to start searching contributions." + + flag "--to=", + description: "Date (ISO-8601 format) to stop searching contributions." + + named_args number: 1 + end + end + + sig { void } + def contributions + args = contributions_args.parse + + commits = 0 + coauthorships = 0 + + all_repos = args.repositories.nil? || args.repositories.include?("all") + repos = all_repos ? SUPPORTED_REPOS : args.repositories + + repos.each do |repo| + if SUPPORTED_REPOS.exclude?(repo) + return ofail "Unsupported repository: #{repo}. Try one of #{SUPPORTED_REPOS.join(", ")}." + end + + repo_path = find_repo_path_for_repo(repo) + unless repo_path.exist? + + opoo "Repository #{repo} not yet tapped! Tapping it now..." + Tap.fetch("homebrew", repo).install + end + + commits += git_log_author_cmd(T.must(repo_path), args) + coauthorships += git_log_coauthor_cmd(T.must(repo_path), args) + end + + sentence = "#{args.named.first} directly authored #{commits} commits " \ + "and co-authored #{coauthorships} commits " \ + "across #{all_repos ? "all Homebrew repos" : repos.to_sentence}" + sentence += if args.from && args.to + " between #{args.from} and #{args.to}" + elsif args.from + " after #{args.from}" + elsif args.to + " before #{args.to}" + else + " in all time" + end + sentence += "." + + puts sentence + end + + sig { params(repo: String).returns(Pathname) } + def find_repo_path_for_repo(repo) + return HOMEBREW_REPOSITORY if repo == "brew" + + Tap.fetch("homebrew", repo).path + end + + sig { params(repo_path: Pathname, args: Homebrew::CLI::Args).returns(Integer) } + def git_log_author_cmd(repo_path, args) + cmd = ["git", "-C", repo_path, "log", "--oneline", "--author=#{args.named.first}"] + cmd << "--before=#{args.to}" if args.to + cmd << "--after=#{args.from}" if args.from + + Utils.safe_popen_read(*cmd).lines.count + end + + sig { params(repo_path: Pathname, args: Homebrew::CLI::Args).returns(Integer) } + def git_log_coauthor_cmd(repo_path, args) + cmd = ["git", "-C", repo_path, "log", "--oneline"] + cmd << "--format='%(trailers:key=Co-authored-by:)'" + cmd << "--before=#{args.to}" if args.to + cmd << "--after=#{args.from}" if args.from + + Utils.safe_popen_read(*cmd).lines.count { |l| l.include?(args.named.first) } + end +end diff --git a/Library/Homebrew/test/dev-cmd/contributions_spec.rb b/Library/Homebrew/test/dev-cmd/contributions_spec.rb new file mode 100644 index 0000000000..bd60333dc0 --- /dev/null +++ b/Library/Homebrew/test/dev-cmd/contributions_spec.rb @@ -0,0 +1,8 @@ +# typed: false +# frozen_string_literal: true + +require "cmd/shared_examples/args_parse" + +describe "brew contributions" do + it_behaves_like "parseable arguments" +end