mirror of
https://github.com/Homebrew/brew.git
synced 2025-07-14 16:09:03 +08:00

When users don't have `HOMEBREW_NO_ANALYTICS` or `HOMEBREW_NO_GITHUB_API` set let's display some analytics data in `brew info`. This should be useful for both maintainers and for users of Homebrew. Note this by default combines all installs across options for a single number; for formulae with lots of options it's a bit overwhelming to print the installs per-option. However, for `HOMEBREW_DEVELOPER`s print the full output. Sample non-developer output: ```console $ brew info wget wget: stable 1.19.5 (bottled), HEAD Internet file retriever https://www.gnu.org/software/wget/ /usr/local/Cellar/wget/1.19.5 (49 files, 3.7MB) * Built from source on 2018-09-03 at 20:46:32 From: https://github.com/Homebrew/homebrew-core/blob/master/Formula/wget.rb ==> Dependencies Build: pkg-config ✔ Required: libidn2 ✔, openssl ✔ Optional: pcre ✔, libmetalink ✘, gpgme ✘ ==> Options --with-debug Build with debug support --with-gpgme Build with gpgme support --with-libmetalink Build with libmetalink support --with-pcre Build with pcre support --HEAD Install HEAD version ==> Analytics install: 84638 (30d), 353800 (90d), 1372775 (365d) install_on_request: 77926 (30d), 291305 (90d), 1044898 (365d) build_error: 11 (30d) ``` Sample developer output: ```console $ brew info wget wget: stable 1.19.5 (bottled), HEAD Internet file retriever https://www.gnu.org/software/wget/ /usr/local/Cellar/wget/1.19.5 (49 files, 3.7MB) * Built from source on 2018-09-03 at 20:46:32 From: https://github.com/Homebrew/homebrew-core/blob/master/Formula/wget.rb ==> Dependencies Build: pkg-config ✔ Required: libidn2 ✔, openssl ✔ Optional: pcre ✔, libmetalink ✘, gpgme ✘ ==> Options --with-debug Build with debug support --with-gpgme Build with gpgme support --with-libmetalink Build with libmetalink support --with-pcre Build with pcre support --HEAD Install HEAD version ==> Analytics ==> install (30d) wget: 84516 wget --with-debug: 51 wget --with-libressl: 16 wget --with-pcre: 14 wget --with-pcre --with-libmetalink --with-gpgme: 12 wget --with-debug --with-pcre --with-libmetalink --with-gpgme: 8 wget --HEAD: 3 wget --HEAD --with-debug --with-libmetalink --with-gpgme: 3 wget --with-gpgme: 3 wget --with-libmetalink: 3 wget --with-pcre --with-libmetalink: 3 wget --with-debug --with-pcre: 2 wget --with-libmetalink --with-gpgme: 2 wget --with-pcre --with-gpgme: 2 ==> install (90d) wget: 353131 wget --with-debug: 188 wget --with-pcre: 138 wget --with-pcre --with-libmetalink --with-gpgme: 118 wget --with-libressl: 81 wget --with-debug --with-pcre --with-libmetalink --with-gpgme: 47 wget --with-pcre --with-libmetalink: 31 wget --HEAD: 13 wget --with-pcre --with-gpgme: 12 wget --with-gpgme: 11 wget --with-debug --with-pcre: 10 wget --with-libmetalink: 8 wget --HEAD --with-pcre --with-libmetalink --with-gpgme: 4 wget --with-debug --with-pcre --with-libmetalink: 4 wget --with-libmetalink --with-gpgme: 4 ==> install (365d) wget: 1369530 wget --with-pcre: 810 wget --with-debug: 649 wget --with-pcre --with-libmetalink --with-gpgme: 554 wget --with-libressl: 479 wget --with-debug --with-pcre --with-libmetalink --with-gpgme: 235 wget --with-pcre --with-libmetalink: 184 wget --with-gpgme: 67 wget --with-pcre --with-gpgme: 67 wget --with-debug --with-pcre: 65 wget --HEAD: 54 wget --with-libmetalink: 30 wget --with-libmetalink --with-gpgme: 27 wget --with-debug --with-pcre --with-libmetalink: 24 ==> install_on_request (30d) wget: 77827 wget --with-debug: 48 wget --with-pcre: 12 wget --with-pcre --with-libmetalink --with-gpgme: 11 wget --with-debug --with-pcre --with-libmetalink --with-gpgme: 8 wget --HEAD: 3 wget --HEAD --with-debug --with-libmetalink --with-gpgme: 3 wget --with-gpgme: 3 wget --with-libmetalink: 3 wget --with-debug --with-pcre: 2 wget --with-libmetalink --with-gpgme: 2 wget --with-pcre --with-gpgme: 2 wget --with-pcre --with-libmetalink: 2 ==> install_on_request (90d) wget: 290818 wget --with-debug: 157 wget --with-pcre --with-libmetalink --with-gpgme: 101 wget --with-pcre: 100 wget --with-debug --with-pcre --with-libmetalink --with-gpgme: 42 wget --with-pcre --with-libmetalink: 30 wget --HEAD: 13 wget --with-pcre --with-gpgme: 11 wget --with-gpgme: 10 wget --with-debug --with-pcre: 8 wget --with-libmetalink: 7 wget --HEAD --with-pcre --with-libmetalink --with-gpgme: 4 wget --with-debug --with-pcre --with-libmetalink: 4 ==> install_on_request (365d) wget: 1042845 wget --with-pcre: 504 wget --with-debug: 458 wget --with-pcre --with-libmetalink --with-gpgme: 432 wget --with-debug --with-pcre --with-libmetalink --with-gpgme: 201 wget --with-pcre --with-libmetalink: 158 wget --with-gpgme: 61 wget --HEAD: 54 wget --with-pcre --with-gpgme: 49 wget --with-debug --with-pcre: 47 wget --with-debug --with-pcre --with-libmetalink: 24 wget --with-libressl: 23 wget --with-libmetalink: 22 wget --with-libmetalink --with-gpgme: 20 ==> build_error (30d) wget: 9 wget --HEAD: 1 wget --with-debug: 1 ```
249 lines
6.4 KiB
Ruby
249 lines
6.4 KiB
Ruby
#: * `info`:
|
|
#: Display brief statistics for your Homebrew installation.
|
|
#:
|
|
#: * `info` <formula> (`--verbose`):
|
|
#: Display information about <formula> and analytics data (provided neither
|
|
#: `HOMEBREW_NO_ANALYTICS` or `HOMEBREW_NO_GITHUB_API` are set)
|
|
#:
|
|
#: Pass `--verbose` to see more detailed analytics data.
|
|
#:
|
|
#: * `info` `--github` <formula>:
|
|
#: Open a browser to the GitHub History page for <formula>.
|
|
#:
|
|
#: To view formula history locally: `brew log -p <formula>`
|
|
#:
|
|
#: * `info` `--json=`<version> (`--all`|`--installed`|<formulae>):
|
|
#: Print a JSON representation of <formulae>. Currently the only accepted value
|
|
#: for <version> is `v1`.
|
|
#:
|
|
#: Pass `--all` to get information on all formulae, or `--installed` to get
|
|
#: information on all installed formulae.
|
|
#:
|
|
#: See the docs for examples of using the JSON output:
|
|
#: <https://docs.brew.sh/Querying-Brew>
|
|
|
|
require "missing_formula"
|
|
require "caveats"
|
|
require "options"
|
|
require "formula"
|
|
require "keg"
|
|
require "tab"
|
|
require "json"
|
|
|
|
module Homebrew
|
|
module_function
|
|
|
|
def info
|
|
# eventually we'll solidify an API, but we'll keep old versions
|
|
# awhile around for compatibility
|
|
if ARGV.json == "v1"
|
|
print_json
|
|
elsif ARGV.flag? "--github"
|
|
exec_browser(*ARGV.formulae.map { |f| github_info(f) })
|
|
else
|
|
print_info
|
|
end
|
|
end
|
|
|
|
def print_info
|
|
if ARGV.named.empty?
|
|
if HOMEBREW_CELLAR.exist?
|
|
count = Formula.racks.length
|
|
puts "#{Formatter.pluralize(count, "keg")}, #{HOMEBREW_CELLAR.abv}"
|
|
end
|
|
else
|
|
ARGV.named.each_with_index do |f, i|
|
|
puts unless i.zero?
|
|
begin
|
|
if f.include?("/") || File.exist?(f)
|
|
info_formula Formulary.factory(f)
|
|
else
|
|
info_formula Formulary.find_with_priority(f)
|
|
end
|
|
rescue FormulaUnavailableError => e
|
|
ofail e.message
|
|
# No formula with this name, try a missing formula lookup
|
|
if (reason = MissingFormula.reason(f))
|
|
$stderr.puts reason
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
def print_json
|
|
ff = if ARGV.include? "--all"
|
|
Formula.sort
|
|
elsif ARGV.include? "--installed"
|
|
Formula.installed.sort
|
|
else
|
|
ARGV.formulae
|
|
end
|
|
json = ff.map(&:to_hash)
|
|
puts JSON.generate(json)
|
|
end
|
|
|
|
def github_remote_path(remote, path)
|
|
if remote =~ %r{^(?:https?://|git(?:@|://))github\.com[:/](.+)/(.+?)(?:\.git)?$}
|
|
"https://github.com/#{Regexp.last_match(1)}/#{Regexp.last_match(2)}/blob/master/#{path}"
|
|
else
|
|
"#{remote}/#{path}"
|
|
end
|
|
end
|
|
|
|
def github_info(f)
|
|
if f.tap
|
|
if remote = f.tap.remote
|
|
path = f.path.relative_path_from(f.tap.path)
|
|
github_remote_path(remote, path)
|
|
else
|
|
f.path
|
|
end
|
|
else
|
|
f.path
|
|
end
|
|
end
|
|
|
|
def info_formula(f)
|
|
specs = []
|
|
|
|
if stable = f.stable
|
|
s = "stable #{stable.version}"
|
|
s += " (bottled)" if stable.bottled?
|
|
specs << s
|
|
end
|
|
|
|
if devel = f.devel
|
|
s = "devel #{devel.version}"
|
|
s += " (bottled)" if devel.bottled?
|
|
specs << s
|
|
end
|
|
|
|
specs << "HEAD" if f.head
|
|
|
|
attrs = []
|
|
attrs << "pinned at #{f.pinned_version}" if f.pinned?
|
|
attrs << "keg-only" if f.keg_only?
|
|
|
|
puts "#{f.full_name}: #{specs * ", "}#{" [#{attrs * ", "}]" unless attrs.empty?}"
|
|
puts f.desc if f.desc
|
|
puts Formatter.url(f.homepage) if f.homepage
|
|
|
|
conflicts = f.conflicts.map do |c|
|
|
reason = " (because #{c.reason})" if c.reason
|
|
"#{c.name}#{reason}"
|
|
end.sort!
|
|
unless conflicts.empty?
|
|
puts <<~EOS
|
|
Conflicts with:
|
|
#{conflicts.join("\n ")}
|
|
EOS
|
|
end
|
|
|
|
kegs = f.installed_kegs
|
|
heads, versioned = kegs.partition { |k| k.version.head? }
|
|
kegs = [
|
|
*heads.sort_by { |k| -Tab.for_keg(k).time.to_i },
|
|
*versioned.sort_by(&:version),
|
|
]
|
|
if kegs.empty?
|
|
puts "Not installed"
|
|
else
|
|
kegs.each do |keg|
|
|
puts "#{keg} (#{keg.abv})#{" *" if keg.linked?}"
|
|
tab = Tab.for_keg(keg).to_s
|
|
puts " #{tab}" unless tab.empty?
|
|
end
|
|
end
|
|
|
|
puts "From: #{Formatter.url(github_info(f))}"
|
|
|
|
unless f.deps.empty?
|
|
ohai "Dependencies"
|
|
%w[build required recommended optional].map do |type|
|
|
deps = f.deps.send(type).uniq
|
|
puts "#{type.capitalize}: #{decorate_dependencies deps}" unless deps.empty?
|
|
end
|
|
end
|
|
|
|
unless f.requirements.to_a.empty?
|
|
ohai "Requirements"
|
|
%w[build required recommended optional].map do |type|
|
|
reqs = f.requirements.select(&:"#{type}?")
|
|
next if reqs.to_a.empty?
|
|
puts "#{type.capitalize}: #{decorate_requirements(reqs)}"
|
|
end
|
|
end
|
|
|
|
if !f.options.empty? || f.head || f.devel
|
|
ohai "Options"
|
|
Homebrew.dump_options_for_formula f
|
|
end
|
|
|
|
caveats = Caveats.new(f)
|
|
ohai "Caveats", caveats.to_s unless caveats.empty?
|
|
|
|
output_analytics(f)
|
|
end
|
|
|
|
def output_analytics(f)
|
|
return if ENV["HOMEBREW_NO_ANALYTICS"]
|
|
return if ENV["HOMEBREW_NO_GITHUB_API"]
|
|
|
|
formulae_json_url = "https://formulae.brew.sh/api/formula/#{f}.json"
|
|
output, = curl_output("--max-time", "3", formulae_json_url)
|
|
return if output.empty?
|
|
|
|
json = begin
|
|
JSON.parse(output)
|
|
rescue JSON::ParserError
|
|
nil
|
|
end
|
|
return if json.nil? || json.empty? || json["analytics"].empty?
|
|
|
|
ohai "Analytics"
|
|
if ARGV.verbose?
|
|
json["analytics"].each do |category, value|
|
|
value.each do |range, results|
|
|
oh1 "#{category} (#{range})"
|
|
results.each do |name_with_options, count|
|
|
puts "#{name_with_options}: #{count}"
|
|
end
|
|
end
|
|
end
|
|
return
|
|
end
|
|
|
|
json["analytics"].each do |category, value|
|
|
analytics = value.map do |range, results|
|
|
"#{results.values.inject("+")} (#{range})"
|
|
end
|
|
puts "#{category}: #{analytics.join(", ")}"
|
|
end
|
|
end
|
|
|
|
def decorate_dependencies(dependencies)
|
|
deps_status = dependencies.map do |dep|
|
|
if dep.satisfied?([])
|
|
pretty_installed(dep_display_s(dep))
|
|
else
|
|
pretty_uninstalled(dep_display_s(dep))
|
|
end
|
|
end
|
|
deps_status.join(", ")
|
|
end
|
|
|
|
def decorate_requirements(requirements)
|
|
req_status = requirements.map do |req|
|
|
req_s = req.display_s
|
|
req.satisfied? ? pretty_installed(req_s) : pretty_uninstalled(req_s)
|
|
end
|
|
req_status.join(", ")
|
|
end
|
|
|
|
def dep_display_s(dep)
|
|
return dep.name if dep.option_tags.empty?
|
|
"#{dep.name} #{dep.option_tags.map { |o| "--#{o}" }.join(" ")}"
|
|
end
|
|
end
|