Skip to content

Commit

Permalink
dev-cmd/contributions: Stats for all maintainers
Browse files Browse the repository at this point in the history
- With `brew contributions`, this will output a list of stats
  (across the specified time period, or all time) for people in the
  "maintainers" team on GitHub.
- Add a `--user` flag for getting stats for a specific user (either
  username, name or email address).
- This assumes that their Git committer details are the same as their name is
  set to on GitHub.
- Show an error message if trying to generate a CSV for the full maintainer
  list, since I haven't worked out how to best show all of that info yet (or
  even how best to show only the totals across everything for every user) in
  that format.
  • Loading branch information
issyl0 committed Feb 22, 2023
1 parent 5272640 commit 214110f
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 34 deletions.
3 changes: 3 additions & 0 deletions Library/Homebrew/cli/args.rbi
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,9 @@ module Homebrew

sig { returns(T::Boolean) }
def csv?; end

sig { returns(T.nilable(String)) }
def user; end
end
end
end
91 changes: 57 additions & 34 deletions Library/Homebrew/dev-cmd/contributions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,9 @@ module Homebrew
sig { returns(CLI::Parser) }
def contributions_args
Homebrew::CLI::Parser.new do
usage_banner "`contributions` <email|username> [<--repositories>`=`] [<--csv>]"
usage_banner "`contributions` [--user=<email|username>] [<--repositories>`=`] [<--csv>]"
description <<~EOS
Contributions to Homebrew repos for a user.
The first argument is a GitHub username (e.g. "BrewTestBot") or an email address (e.g. "brewtestbot@brew.sh").
Contributions to Homebrew repos.
EOS

comma_array "--repositories",
Expand All @@ -37,10 +35,11 @@ def contributions_args
flag "--to=",
description: "Date (ISO-8601 format) to stop searching contributions."

flag "--user=",
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."

named_args number: 1
end
end

Expand All @@ -59,33 +58,26 @@ def contributions
args.repositories
end

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)
tap = Tap.fetch("homebrew", repo)
unless repo_path.exist?
opoo "Repository #{repo} not yet tapped! Tapping it now..."
tap.install
end

repo_full_name = if repo == "brew"
"homebrew/brew"
else
tap.full_name
end

results[repo] = {
commits: GitHub.repo_commit_count_for_user(repo_full_name, args.named.first),
coauthorships: git_log_trailers_cmd(T.must(repo_path), "Co-authored-by", args),
signoffs: git_log_trailers_cmd(T.must(repo_path), "Signed-off-by", args),
}
return ofail "CSVs not yet supported for the full list of maintainers at once." if args.csv? && args.user.nil?

maintainers = GitHub.members_by_team("Homebrew", "maintainers")
maintainers.each do |username, _|
puts "Determining contributions for #{username}..." if args.verbose?
# TODO: Using the GitHub username to scan the `git log` undercounts some
# contributions as people might not always have configured their Git
# committer details to match the ones on GitHub.
# 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)}."
end

puts "The user #{args.named.first} has made #{total(results)} contributions #{time_period(args)}."
puts generate_csv(args.named.first, results) if args.csv?
return unless 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?
end

sig { params(repo: String).returns(Pathname) }
Expand Down Expand Up @@ -126,6 +118,37 @@ def generate_csv(user, results)
end
end

def scan_repositories(repos, person, args)
data = {}

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)
tap = Tap.fetch("homebrew", repo)
unless repo_path.exist?
opoo "Repository #{repo} not yet tapped! Tapping it now..."
tap.install
end

repo_full_name = if repo == "brew"
"homebrew/brew"
else
tap.full_name
end

data[repo] = {
commits: GitHub.repo_commit_count_for_user(repo_full_name, person),
coauthorships: git_log_trailers_cmd(T.must(repo_path), "Co-authored-by", person, args),
signoffs: git_log_trailers_cmd(T.must(repo_path), "Signed-off-by", person, args),
}
end

data
end

sig { params(results: Hash).returns(Integer) }
def total(results)
results
Expand All @@ -134,13 +157,13 @@ def total(results)
.sum(&:sum) # 956
end

sig { params(repo_path: Pathname, trailer: String, args: Homebrew::CLI::Args).returns(Integer) }
def git_log_trailers_cmd(repo_path, trailer, args)
sig { params(repo_path: Pathname, person: String, trailer: String, args: Homebrew::CLI::Args).returns(Integer) }
def git_log_trailers_cmd(repo_path, person, trailer, args)
cmd = ["git", "-C", repo_path, "log", "--oneline"]
cmd << "--format='%(trailers:key=#{trailer}:)'"
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) }
Utils.safe_popen_read(*cmd).lines.count { |l| l.include?(person) }
end
end

0 comments on commit 214110f

Please sign in to comment.