Move more code to utils/analytics.

This commit is contained in:
Mike McQuaid 2019-11-22 09:08:31 +00:00
parent 727f9671c7
commit 3a9f585ebb
No known key found for this signature in database
GPG Key ID: 48A898132FD8EE70
11 changed files with 321 additions and 286 deletions

View File

@ -24,36 +24,23 @@ module Homebrew
def analytics
analytics_args.parse
config_file = HOMEBREW_REPOSITORY/".git/config"
raise UsageError if args.remaining.size > 1
case args.remaining.first
when nil, "state"
analyticsdisabled =
Utils.popen_read("git config --file=#{config_file} --get homebrew.analyticsdisabled").chomp
uuid =
Utils.popen_read("git config --file=#{config_file} --get homebrew.analyticsuuid").chomp
if ENV["HOMEBREW_NO_ANALYTICS"]
puts "Analytics is disabled (by HOMEBREW_NO_ANALYTICS)."
elsif analyticsdisabled == "true"
puts "Analytics is disabled."
if Utils::Analytics.disabled?
puts "Analytics are disabled."
else
puts "Analytics is enabled."
puts "UUID: #{uuid}" if uuid.present?
puts "Analytics are enabled."
puts "UUID: #{Utils::Analytics.uuid}" if Utils::Analytics.uuid.present?
end
when "on"
safe_system "git", "config", "--file=#{config_file}",
"--replace-all", "homebrew.analyticsdisabled", "false"
safe_system "git", "config", "--file=#{config_file}",
"--replace-all", "homebrew.analyticsmessage", "true"
Utils::Analytics.enable!
when "off"
safe_system "git", "config", "--file=#{config_file}",
"--replace-all", "homebrew.analyticsdisabled", "true"
system "git", "config", "--file=#{config_file}", "--unset-all", "homebrew.analyticsuuid"
Utils::Analytics.disable!
when "regenerate-uuid"
# it will be regenerated in next run.
system "git", "config", "--file=#{config_file}", "--unset-all", "homebrew.analyticsuuid"
Utils::Analytics.regenerate_uuid!
else
raise UsageError
end

View File

@ -89,7 +89,7 @@ module Homebrew
def print_info
if ARGV.named.empty?
if args.analytics?
output_analytics
Utils::Analytics.output
elsif HOMEBREW_CELLAR.exist?
count = Formula.racks.length
puts "#{count} #{"keg".pluralize(count)}, #{HOMEBREW_CELLAR.dup.abv}"
@ -104,13 +104,13 @@ module Homebrew
Formulary.find_with_priority(f)
end
if args.analytics?
output_formula_analytics(formula)
Utils::Analytics.formula_output(formula)
else
info_formula(formula)
end
rescue FormulaUnavailableError => e
if args.analytics?
output_analytics(filter: f)
Utils::Analytics.output(filter: f)
next
end
ofail e.message
@ -234,169 +234,7 @@ module Homebrew
caveats = Caveats.new(f)
ohai "Caveats", caveats.to_s unless caveats.empty?
output_formula_analytics(f)
end
def formulae_api_json(endpoint)
return if ENV["HOMEBREW_NO_ANALYTICS"] || ENV["HOMEBREW_NO_GITHUB_API"]
output, = curl_output("--max-time", "5",
"https://formulae.brew.sh/api/#{endpoint}")
return if output.blank?
JSON.parse(output)
rescue JSON::ParserError
nil
end
def analytics_table(category, days, results, os_version: false, cask_install: false)
oh1 "#{category} (#{days} days)"
total_count = results.values.inject("+")
formatted_total_count = format_count(total_count)
formatted_total_percent = format_percent(100)
index_header = "Index"
count_header = "Count"
percent_header = "Percent"
name_with_options_header = if os_version
"macOS Version"
elsif cask_install
"Token"
else
"Name (with options)"
end
total_index_footer = "Total"
max_index_width = results.length.to_s.length
index_width = [
index_header.length,
total_index_footer.length,
max_index_width,
].max
count_width = [
count_header.length,
formatted_total_count.length,
].max
percent_width = [
percent_header.length,
formatted_total_percent.length,
].max
name_with_options_width = Tty.width -
index_width -
count_width -
percent_width -
10 # spacing and lines
formatted_index_header =
format "%#{index_width}s", index_header
formatted_name_with_options_header =
format "%-#{name_with_options_width}s",
name_with_options_header[0..name_with_options_width-1]
formatted_count_header =
format "%#{count_width}s", count_header
formatted_percent_header =
format "%#{percent_width}s", percent_header
puts "#{formatted_index_header} | #{formatted_name_with_options_header} | "\
"#{formatted_count_header} | #{formatted_percent_header}"
columns_line = "#{"-"*index_width}:|-#{"-"*name_with_options_width}-|-"\
"#{"-"*count_width}:|-#{"-"*percent_width}:"
puts columns_line
index = 0
results.each do |name_with_options, count|
index += 1
formatted_index = format "%0#{max_index_width}d", index
formatted_index = format "%-#{index_width}s", formatted_index
formatted_name_with_options =
format "%-#{name_with_options_width}s",
name_with_options[0..name_with_options_width-1]
formatted_count = format "%#{count_width}s", format_count(count)
formatted_percent = if total_count.zero?
format "%#{percent_width}s", format_percent(0)
else
format "%#{percent_width}s",
format_percent((count.to_i * 100) / total_count.to_f)
end
puts "#{formatted_index} | #{formatted_name_with_options} | " \
"#{formatted_count} | #{formatted_percent}%"
next if index > 10
end
return unless results.length > 1
formatted_total_footer =
format "%-#{index_width}s", total_index_footer
formatted_blank_footer =
format "%-#{name_with_options_width}s", ""
formatted_total_count_footer =
format "%#{count_width}s", formatted_total_count
formatted_total_percent_footer =
format "%#{percent_width}s", formatted_total_percent
puts "#{formatted_total_footer} | #{formatted_blank_footer} | "\
"#{formatted_total_count_footer} | #{formatted_total_percent_footer}%"
end
def output_analytics(filter: nil)
days = args.days || "30"
category = args.category || "install"
json = formulae_api_json("analytics/#{category}/#{days}d.json")
return if json.blank? || json["items"].blank?
os_version = category == "os-version"
cask_install = category == "cask-install"
results = {}
json["items"].each do |item|
key = if os_version
item["os_version"]
elsif cask_install
item["cask"]
else
item["formula"]
end
if filter.present?
next if key != filter && !key.start_with?("#{filter} ")
end
results[key] = item["count"].tr(",", "").to_i
end
if filter.present? && results.blank?
onoe "No results matching `#{filter}` found!"
return
end
analytics_table(category, days, results, os_version: os_version, cask_install: cask_install)
end
def output_formula_analytics(f)
json = formulae_api_json("#{formula_path}/#{f}.json")
return if json.blank? || json["analytics"].blank?
full_analytics = args.analytics? || args.verbose?
ohai "Analytics"
json["analytics"].each do |category, value|
category = category.tr("_", "-")
analytics = []
value.each do |days, results|
days = days.to_i
if full_analytics
if args.days.present?
next if args.days&.to_i != days
end
if args.category.present?
next if args.category != category
end
analytics_table(category, days, results)
else
total_count = results.values.inject("+")
analytics << "#{number_readable(total_count)} (#{days} days)"
end
end
puts "#{category}: #{analytics.join(", ")}" unless full_analytics
end
Utils::Analytics.formula_output(f)
end
def decorate_dependencies(dependencies)
@ -423,24 +261,4 @@ module Homebrew
"#{dep.name} #{dep.option_tags.map { |o| "--#{o}" }.join(" ")}"
end
def format_count(count)
count.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
end
def format_percent(percent)
format("%<percent>.2f", percent: percent)
end
def formula_path
"formula"
end
alias_method :generic_formula_path, :formula_path
def analytics_path
"analytics"
end
alias_method :generic_analytics_path, :analytics_path
require "extend/os/cmd/info"
end

View File

@ -38,38 +38,27 @@ module Homebrew
def update_report
update_report_args.parse
if !Utils::Analytics.messages_displayed? &&
!Utils::Analytics.disabled? &&
!Utils::Analytics.no_message_output?
ENV["HOMEBREW_NO_ANALYTICS_THIS_RUN"] = "1"
# Use the shell's audible bell.
print "\a"
# Use an extra newline and bold to avoid this being missed.
ohai "Homebrew has enabled anonymous aggregate formulae and cask analytics."
puts <<~EOS
#{Tty.bold}Read the analytics documentation (and how to opt-out) here:
#{Formatter.url("https://docs.brew.sh/Analytics")}#{Tty.reset}
EOS
# Consider the messages possibly missed if not a TTY.
Utils::Analytics.messages_displayed! if $stdout.tty?
end
HOMEBREW_REPOSITORY.cd do
analytics_message_displayed =
Utils.popen_read("git", "config", "--get", "homebrew.analyticsmessage").chomp == "true"
cask_analytics_message_displayed =
Utils.popen_read("git", "config", "--get", "homebrew.caskanalyticsmessage").chomp == "true"
analytics_disabled =
Utils.popen_read("git", "config", "--get", "homebrew.analyticsdisabled").chomp == "true"
if !analytics_message_displayed &&
!cask_analytics_message_displayed &&
!analytics_disabled &&
!ENV["HOMEBREW_NO_ANALYTICS"] &&
!ENV["HOMEBREW_NO_ANALYTICS_MESSAGE_OUTPUT"]
ENV["HOMEBREW_NO_ANALYTICS_THIS_RUN"] = "1"
# Use the shell's audible bell.
print "\a"
# Use an extra newline and bold to avoid this being missed.
ohai "Homebrew has enabled anonymous aggregate formulae and cask analytics."
puts <<~EOS
#{Tty.bold}Read the analytics documentation (and how to opt-out) here:
#{Formatter.url("https://docs.brew.sh/Analytics")}#{Tty.reset}
EOS
# Consider the message possibly missed if not a TTY.
if $stdout.tty?
safe_system "git", "config", "--replace-all", "homebrew.analyticsmessage", "true"
safe_system "git", "config", "--replace-all", "homebrew.caskanalyticsmessage", "true"
end
end
donation_message_displayed =
Utils.popen_read("git", "config", "--get", "homebrew.donationmessage").chomp == "true"
unless donation_message_displayed

View File

@ -1,3 +0,0 @@
# frozen_string_literal: true
require "extend/os/linux/info" if OS.linux?

View File

@ -1,17 +0,0 @@
# frozen_string_literal: true
module Homebrew
module_function
def formula_path
return generic_formula_path if ENV["HOMEBREW_FORCE_HOMEBREW_ON_LINUX"]
"formula-linux"
end
def analytics_path
return generic_analytics_path if ENV["HOMEBREW_FORCE_HOMEBREW_ON_LINUX"]
"analytics-linux"
end
end

View File

@ -0,0 +1,19 @@
# frozen_string_literal: true
module Utils
module Analytics
class << self
def formula_path
return generic_formula_path if ENV["HOMEBREW_FORCE_HOMEBREW_ON_LINUX"]
"formula-linux"
end
def analytics_path
return generic_analytics_path if ENV["HOMEBREW_FORCE_HOMEBREW_ON_LINUX"]
"analytics-linux"
end
end
end
end

View File

@ -1,3 +1,4 @@
# frozen_string_literal: true
require "extend/os/linux/utils/analytics" if OS.linux?
require "extend/os/mac/utils/analytics" if OS.mac?

View File

@ -14,7 +14,7 @@ describe "brew analytics", :integration_test do
brew "analytics", "off"
expect { brew "analytics", "HOMEBREW_NO_ANALYTICS" => nil }
.to output(/Analytics is disabled/).to_stdout
.to output(/Analytics are disabled/).to_stdout
.and not_to_output.to_stderr
.and be_a_success
end

View File

@ -22,13 +22,6 @@ end
describe Homebrew do
let(:remote) { "https://github.com/Homebrew/homebrew-core" }
specify "::analytics_table" do
results = { ack: 10, wget: 100 }
expect { subject.analytics_table("install", "30", results) }
.to output(/110 | 100.00%/).to_stdout
.and not_to_output.to_stderr
end
specify "::github_remote_path" do
expect(subject.github_remote_path(remote, "Formula/git.rb"))
.to eq("https://github.com/Homebrew/homebrew-core/blob/master/Formula/git.rb")

View File

@ -86,4 +86,11 @@ describe Utils::Analytics do
end
end
end
specify "::table_output" do
results = { ack: 10, wget: 100 }
expect { described_class.table_output("install", "30", results) }
.to output(/110 | 100.00%/).to_stdout
.and not_to_output.to_stderr
end
end

View File

@ -5,27 +5,8 @@ require "erb"
module Utils
module Analytics
class << self
def custom_prefix_label
"custom-prefix"
end
def clear_os_prefix_ci
return unless instance_variable_defined?(:@os_prefix_ci)
remove_instance_variable(:@os_prefix_ci)
end
def os_prefix_ci
@os_prefix_ci ||= begin
os = OS_VERSION
prefix = ", #{custom_prefix_label}" unless Homebrew.default_prefix?
ci = ", CI" if ENV["CI"]
"#{os}#{prefix}#{ci}"
end
end
def report(type, metadata = {})
return if ENV["HOMEBREW_NO_ANALYTICS"] || ENV["HOMEBREW_NO_ANALYTICS_THIS_RUN"]
return if disabled?
args = []
@ -90,8 +71,268 @@ module Utils
end
report_event("BuildError", action)
end
def messages_displayed?
config_true?(:analyticsmessage) && config_true?(:caskanalyticsmessage)
end
def disabled?
return true if ENV["HOMEBREW_NO_ANALYTICS"] || ENV["HOMEBREW_NO_ANALYTICS_THIS_RUN"]
config_true?(:analyticsdisabled)
end
def no_message_output?
# Used by Homebrew/install
ENV["HOMEBREW_NO_ANALYTICS_MESSAGE_OUTPUT"].present?
end
def uuid
config_get(:analyticsuuid)
end
def messages_displayed!
config_set(:analyticsmessage, true)
config_set(:caskanalyticsmessage, true)
end
def enable!
config_set(:analyticsdisabled, false)
messages_displayed!
end
def disable!
config_set(:analyticsdisabled, true)
regenerate_uuid!
end
def regenerate_uuid!
# it will be regenerated in next run unless disabled.
config_delete(:analyticsuuid)
end
def output(filter: nil)
days = Homebrew.args.days || "30"
category = Homebrew.args.category || "install"
json = formulae_api_json("analytics/#{category}/#{days}d.json")
return if json.blank? || json["items"].blank?
os_version = category == "os-version"
cask_install = category == "cask-install"
results = {}
json["items"].each do |item|
key = if os_version
item["os_version"]
elsif cask_install
item["cask"]
else
item["formula"]
end
if filter.present?
next if key != filter && !key.start_with?("#{filter} ")
end
results[key] = item["count"].tr(",", "").to_i
end
if filter.present? && results.blank?
onoe "No results matching `#{filter}` found!"
return
end
table_output(category, days, results, os_version: os_version, cask_install: cask_install)
end
def formula_output(f)
json = formulae_api_json("#{formula_path}/#{f}.json")
return if json.blank? || json["analytics"].blank?
full_analytics = Homebrew.args.analytics? || Homebrew.args.verbose?
ohai "Analytics"
json["analytics"].each do |category, value|
category = category.tr("_", "-")
analytics = []
value.each do |days, results|
days = days.to_i
if full_analytics
if Homebrew.args.days.present?
next if Homebrew.args.days&.to_i != days
end
if Homebrew.args.category.present?
next if Homebrew.args.category != category
end
analytics_table(category, days, results)
else
total_count = results.values.inject("+")
analytics << "#{number_readable(total_count)} (#{days} days)"
end
end
puts "#{category}: #{analytics.join(", ")}" unless full_analytics
end
end
def custom_prefix_label
"custom-prefix"
end
def clear_os_prefix_ci
return unless instance_variable_defined?(:@os_prefix_ci)
remove_instance_variable(:@os_prefix_ci)
end
def os_prefix_ci
@os_prefix_ci ||= begin
os = OS_VERSION
prefix = ", #{custom_prefix_label}" unless Homebrew.default_prefix?
ci = ", CI" if ENV["CI"]
"#{os}#{prefix}#{ci}"
end
end
def table_output(category, days, results, os_version: false, cask_install: false)
oh1 "#{category} (#{days} days)"
total_count = results.values.inject("+")
formatted_total_count = format_count(total_count)
formatted_total_percent = format_percent(100)
index_header = "Index"
count_header = "Count"
percent_header = "Percent"
name_with_options_header = if os_version
"macOS Version"
elsif cask_install
"Token"
else
"Name (with options)"
end
total_index_footer = "Total"
max_index_width = results.length.to_s.length
index_width = [
index_header.length,
total_index_footer.length,
max_index_width,
].max
count_width = [
count_header.length,
formatted_total_count.length,
].max
percent_width = [
percent_header.length,
formatted_total_percent.length,
].max
name_with_options_width = Tty.width -
index_width -
count_width -
percent_width -
10 # spacing and lines
formatted_index_header =
format "%#{index_width}s", index_header
formatted_name_with_options_header =
format "%-#{name_with_options_width}s",
name_with_options_header[0..name_with_options_width-1]
formatted_count_header =
format "%#{count_width}s", count_header
formatted_percent_header =
format "%#{percent_width}s", percent_header
puts "#{formatted_index_header} | #{formatted_name_with_options_header} | "\
"#{formatted_count_header} | #{formatted_percent_header}"
columns_line = "#{"-"*index_width}:|-#{"-"*name_with_options_width}-|-"\
"#{"-"*count_width}:|-#{"-"*percent_width}:"
puts columns_line
index = 0
results.each do |name_with_options, count|
index += 1
formatted_index = format "%0#{max_index_width}d", index
formatted_index = format "%-#{index_width}s", formatted_index
formatted_name_with_options =
format "%-#{name_with_options_width}s",
name_with_options[0..name_with_options_width-1]
formatted_count = format "%#{count_width}s", format_count(count)
formatted_percent = if total_count.zero?
format "%#{percent_width}s", format_percent(0)
else
format "%#{percent_width}s",
format_percent((count.to_i * 100) / total_count.to_f)
end
puts "#{formatted_index} | #{formatted_name_with_options} | " \
"#{formatted_count} | #{formatted_percent}%"
next if index > 10
end
return unless results.length > 1
formatted_total_footer =
format "%-#{index_width}s", total_index_footer
formatted_blank_footer =
format "%-#{name_with_options_width}s", ""
formatted_total_count_footer =
format "%#{count_width}s", formatted_total_count
formatted_total_percent_footer =
format "%#{percent_width}s", formatted_total_percent
puts "#{formatted_total_footer} | #{formatted_blank_footer} | "\
"#{formatted_total_count_footer} | #{formatted_total_percent_footer}%"
end
def config_true?(key)
config_get(key) == "true"
end
def config_get(key)
HOMEBREW_REPOSITORY.cd do
Utils.popen_read("git", "config", "--get", "homebrew.#{key}").chomp
end
end
def config_set(key, value)
HOMEBREW_REPOSITORY.cd do
safe_system "git", "config", "--replace-all", "homebrew.#{key}", value.to_s
end
end
def config_delete(key)
HOMEBREW_REPOSITORY.cd do
system "git", "config", "--unset-all", "homebrew.#{key}"
end
end
def formulae_api_json(endpoint)
return if ENV["HOMEBREW_NO_ANALYTICS"] || ENV["HOMEBREW_NO_GITHUB_API"]
output, = curl_output("--max-time", "5",
"https://formulae.brew.sh/api/#{endpoint}")
return if output.blank?
JSON.parse(output)
rescue JSON::ParserError
nil
end
def format_count(count)
count.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
end
def format_percent(percent)
format("%<percent>.2f", percent: percent)
end
def formula_path
"formula"
end
alias generic_formula_path formula_path
def analytics_path
"analytics"
end
alias generic_analytics_path analytics_path
end
end
end
require "extend/os/analytics"
require "extend/os/utils/analytics"