| 
									
										
										
										
											2023-03-07 17:24:20 -08:00
										 |  |  | # typed: true | 
					
						
							| 
									
										
										
										
											2019-04-19 15:38:03 +09:00
										 |  |  | # frozen_string_literal: true | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-23 10:14:18 +01:00
										 |  |  | require "context" | 
					
						
							| 
									
										
										
										
											2016-08-09 19:18:43 +01:00
										 |  |  | require "erb" | 
					
						
							| 
									
										
										
										
											2021-01-12 16:27:25 -05:00
										 |  |  | require "settings" | 
					
						
							| 
									
										
										
										
											2021-08-06 02:30:44 -04:00
										 |  |  | require "api" | 
					
						
							| 
									
										
										
										
											2016-08-09 19:18:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-03 14:21:08 +01:00
										 |  |  | module Utils | 
					
						
							| 
									
										
										
										
											2020-08-19 07:43:40 +02:00
										 |  |  |   # Helper module for fetching and reporting analytics data. | 
					
						
							|  |  |  |   # | 
					
						
							|  |  |  |   # @api private | 
					
						
							| 
									
										
										
										
											2016-05-03 14:21:08 +01:00
										 |  |  |   module Analytics | 
					
						
							| 
									
										
										
										
											2023-02-21 17:07:01 +00:00
										 |  |  |     INFLUX_BUCKET = "analytics" | 
					
						
							| 
									
										
										
										
											2023-03-20 15:26:47 +00:00
										 |  |  |     INFLUX_TOKEN = "sSE5_ENBUUhuh3vL3QDi6Rqo96DDZznBYoBT_TEdYnjj8IH2H_1PQD2qkAP0nnSwEIKvfQvW3Sb24GWYT35jqg==" | 
					
						
							| 
									
										
										
										
											2023-02-21 17:07:01 +00:00
										 |  |  |     INFLUX_HOST = "https://europe-west1-1.gcp.cloud2.influxdata.com" | 
					
						
							|  |  |  |     INFLUX_ORG = "9a707721bb47fc02" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-03 14:21:08 +01:00
										 |  |  |     class << self | 
					
						
							| 
									
										
										
										
											2020-10-20 12:03:48 +02:00
										 |  |  |       extend T::Sig | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-02 14:32:31 +02:00
										 |  |  |       include Context | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-15 16:34:50 +00:00
										 |  |  |       sig { params(type: Symbol, metadata: T::Hash[Symbol, T.untyped]).void } | 
					
						
							| 
									
										
										
										
											2022-05-31 18:03:48 +02:00
										 |  |  |       def report_google(type, metadata = {}) | 
					
						
							| 
									
										
										
										
											2021-10-13 13:40:54 -05:00
										 |  |  |         analytics_ids = ENV.fetch("HOMEBREW_ANALYTICS_IDS", "").split(",") | 
					
						
							|  |  |  |         analytics_ids.each do |analytics_id| | 
					
						
							|  |  |  |           args = [] | 
					
						
							| 
									
										
										
										
											2018-09-14 18:50:03 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-13 13:40:54 -05:00
										 |  |  |           # do not load .curlrc unless requested (must be the first argument) | 
					
						
							|  |  |  |           args << "--disable" unless Homebrew::EnvConfig.curlrc? | 
					
						
							| 
									
										
										
										
											2018-09-14 18:50:03 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-13 13:40:54 -05:00
										 |  |  |           args += %W[
 | 
					
						
							|  |  |  |             --max-time 3
 | 
					
						
							|  |  |  |             --user-agent #{HOMEBREW_USER_AGENT_CURL} | 
					
						
							|  |  |  |             --data v=1
 | 
					
						
							|  |  |  |             --data aip=1
 | 
					
						
							|  |  |  |             --data t=#{type} | 
					
						
							|  |  |  |             --data tid=#{analytics_id} | 
					
						
							| 
									
										
										
										
											2023-02-20 09:05:15 +00:00
										 |  |  |             --data uid=n0thxg00gl3 | 
					
						
							| 
									
										
										
										
											2021-10-13 13:40:54 -05:00
										 |  |  |             --data an=#{HOMEBREW_PRODUCT} | 
					
						
							|  |  |  |             --data av=#{HOMEBREW_VERSION} | 
					
						
							|  |  |  |           ] | 
					
						
							|  |  |  |           metadata.each do |key, value| | 
					
						
							|  |  |  |             next unless value | 
					
						
							| 
									
										
										
										
											2018-09-17 02:45:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-13 13:40:54 -05:00
										 |  |  |             key = ERB::Util.url_encode key | 
					
						
							|  |  |  |             value = ERB::Util.url_encode value | 
					
						
							|  |  |  |             args << "--data" << "#{key}=#{value}" | 
					
						
							|  |  |  |           end | 
					
						
							| 
									
										
										
										
											2016-03-28 09:16:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-30 04:20:53 +01:00
										 |  |  |           curl = Utils::Curl.curl_executable | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-13 13:40:54 -05:00
										 |  |  |           # Send analytics. Don't send or store any personally identifiable information. | 
					
						
							|  |  |  |           # https://docs.brew.sh/Analytics | 
					
						
							|  |  |  |           # https://developers.google.com/analytics/devguides/collection/protocol/v1/devguide | 
					
						
							|  |  |  |           # https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters | 
					
						
							|  |  |  |           if ENV["HOMEBREW_ANALYTICS_DEBUG"] | 
					
						
							|  |  |  |             url = "https://www.google-analytics.com/debug/collect" | 
					
						
							| 
									
										
										
										
											2022-05-30 04:20:53 +01:00
										 |  |  |             puts "#{curl} #{args.join(" ")} #{url}" | 
					
						
							|  |  |  |             puts Utils.popen_read(curl, *args, url) | 
					
						
							| 
									
										
										
										
											2021-10-13 13:40:54 -05:00
										 |  |  |           else | 
					
						
							|  |  |  |             pid = fork do | 
					
						
							| 
									
										
										
										
											2022-05-30 04:20:53 +01:00
										 |  |  |               exec curl, *args, | 
					
						
							| 
									
										
										
										
											2021-10-13 13:40:54 -05:00
										 |  |  |                    "--silent", "--output", "/dev/null", | 
					
						
							| 
									
										
										
										
											2023-01-26 21:24:50 +01:00
										 |  |  |                    "https://www.google-analytics.com/collect" | 
					
						
							| 
									
										
										
										
											2021-10-13 13:40:54 -05:00
										 |  |  |             end | 
					
						
							|  |  |  |             Process.detach T.must(pid) | 
					
						
							| 
									
										
										
										
											2016-05-03 14:21:08 +01:00
										 |  |  |           end | 
					
						
							|  |  |  |         end | 
					
						
							| 
									
										
										
										
											2021-10-13 13:40:54 -05:00
										 |  |  |         nil | 
					
						
							| 
									
										
										
										
											2016-05-03 14:21:08 +01:00
										 |  |  |       end | 
					
						
							| 
									
										
										
										
											2016-03-28 09:16:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-31 18:03:48 +02:00
										 |  |  |       sig { | 
					
						
							| 
									
										
										
										
											2023-03-20 15:26:47 +00:00
										 |  |  |         params(measurement: Symbol, package_name: String, tap_name: String, on_request: T::Boolean, | 
					
						
							|  |  |  |                options: String).void | 
					
						
							| 
									
										
										
										
											2022-05-31 18:03:48 +02:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2023-03-20 15:26:47 +00:00
										 |  |  |       def report_influx(measurement, package_name:, tap_name:, on_request:, options:) | 
					
						
							|  |  |  |         # ensure on_request is a boolean | 
					
						
							| 
									
										
										
										
											2023-02-16 17:51:42 +00:00
										 |  |  |         on_request = on_request ? true : false | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 15:26:47 +00:00
										 |  |  |         # ensure options are removed (by `.compact` below) if empty | 
					
						
							|  |  |  |         options = nil if options.blank? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Tags are always implicitly strings and must have low cardinality. | 
					
						
							|  |  |  |         tags = default_tags_influx.merge(on_request: on_request) | 
					
						
							|  |  |  |                                   .map { |k, v| "#{k}=#{v}" } | 
					
						
							|  |  |  |                                   .join(",") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Fields need explicitly wrapped with quotes and can have high cardinality. | 
					
						
							|  |  |  |         fields = default_fields_influx.merge(package: package_name, tap_name: tap_name, options: options) | 
					
						
							|  |  |  |                                       .compact | 
					
						
							|  |  |  |                                       .map { |k, v| %Q(#{k}="#{v}") } | 
					
						
							|  |  |  |                                       .join(",") | 
					
						
							| 
									
										
										
										
											2022-05-31 18:03:48 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         args = [ | 
					
						
							|  |  |  |           "--max-time", "3", | 
					
						
							| 
									
										
										
										
											2023-03-20 15:26:47 +00:00
										 |  |  |           "--header", "Authorization: Token #{INFLUX_TOKEN}", | 
					
						
							| 
									
										
										
										
											2022-05-31 18:03:48 +02:00
										 |  |  |           "--header", "Content-Type: text/plain; charset=utf-8", | 
					
						
							|  |  |  |           "--header", "Accept: application/json", | 
					
						
							| 
									
										
										
										
											2023-03-20 15:26:47 +00:00
										 |  |  |           "--data-binary", "#{measurement},#{tags} #{fields} #{Time.now.to_i}" | 
					
						
							| 
									
										
										
										
											2022-05-31 18:03:48 +02:00
										 |  |  |         ] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 15:26:47 +00:00
										 |  |  |         # Second precision is highest we can do and has the lowest performance cost. | 
					
						
							| 
									
										
										
										
											2023-02-21 17:07:01 +00:00
										 |  |  |         url = "#{INFLUX_HOST}/api/v2/write?bucket=#{INFLUX_BUCKET}&precision=s" | 
					
						
							| 
									
										
										
										
											2023-02-06 16:56:25 +01:00
										 |  |  |         deferred_curl(url, args) | 
					
						
							| 
									
										
										
										
											2023-02-06 16:28:34 +01:00
										 |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       sig { params(url: String, args: T::Array[String]).void } | 
					
						
							| 
									
										
										
										
											2023-02-06 16:56:25 +01:00
										 |  |  |       def deferred_curl(url, args) | 
					
						
							| 
									
										
										
										
											2023-02-06 16:28:34 +01:00
										 |  |  |         curl = Utils::Curl.curl_executable | 
					
						
							| 
									
										
										
										
											2022-05-31 18:03:48 +02:00
										 |  |  |         if ENV["HOMEBREW_ANALYTICS_DEBUG"] | 
					
						
							|  |  |  |           puts "#{curl} #{args.join(" ")} \"#{url}\"" | 
					
						
							|  |  |  |           puts Utils.popen_read(curl, *args, url) | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |           pid = fork do | 
					
						
							|  |  |  |             exec curl, *args, "--silent", "--output", "/dev/null", url | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |           Process.detach T.must(pid) | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 15:26:47 +00:00
										 |  |  |       sig { | 
					
						
							|  |  |  |         params(measurement: Symbol, package_name: String, tap_name: String, | 
					
						
							|  |  |  |                on_request: T::Boolean, options: String).void | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       def report_event(measurement, package_name:, tap_name:, on_request:, options: "") | 
					
						
							|  |  |  |         report_influx_event(measurement, package_name: package_name, tap_name: tap_name, on_request: on_request, | 
					
						
							|  |  |  | options: options) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         package_and_options = package_name | 
					
						
							|  |  |  |         if tap_name.present? && tap_name != "homebrew/core" && tap_name != "homebrew/cask" | 
					
						
							|  |  |  |           package_and_options = "#{tap_name}/#{package_and_options}" | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |         package_and_options = "#{package_and_options} #{options}" if options.present? | 
					
						
							| 
									
										
										
										
											2023-02-15 16:34:50 +00:00
										 |  |  |         report_google_event(measurement, package_and_options, on_request: on_request) | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-10 16:00:51 +01:00
										 |  |  |       sig { params(category: Symbol, action: String, on_request: T::Boolean).void } | 
					
						
							| 
									
										
										
										
											2023-02-15 16:34:50 +00:00
										 |  |  |       def report_google_event(category, action, on_request: false) | 
					
						
							|  |  |  |         return if not_this_run? || disabled? || Homebrew::EnvConfig.no_google_analytics? | 
					
						
							| 
									
										
										
										
											2022-05-31 18:03:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-15 16:34:50 +00:00
										 |  |  |         category = "install" if category == :formula_install | 
					
						
							| 
									
										
										
										
											2022-05-31 18:03:48 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         report_google(:event, | 
					
						
							| 
									
										
										
										
											2023-02-15 16:34:50 +00:00
										 |  |  |                       ec: category, | 
					
						
							| 
									
										
										
										
											2022-05-31 18:03:48 +02:00
										 |  |  |                       ea: action, | 
					
						
							| 
									
										
										
										
											2023-02-15 16:34:50 +00:00
										 |  |  |                       el: label_google, | 
					
						
							| 
									
										
										
										
											2022-05-31 18:03:48 +02:00
										 |  |  |                       ev: nil) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-15 16:34:50 +00:00
										 |  |  |         return unless on_request | 
					
						
							| 
									
										
										
										
											2022-05-31 18:03:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-15 16:34:50 +00:00
										 |  |  |         report_google(:event, | 
					
						
							|  |  |  |                       ec: :install_on_request, | 
					
						
							|  |  |  |                       ea: action, | 
					
						
							|  |  |  |                       el: label_google, | 
					
						
							|  |  |  |                       ev: nil) | 
					
						
							| 
									
										
										
										
											2016-05-03 14:21:08 +01:00
										 |  |  |       end | 
					
						
							| 
									
										
										
										
											2016-03-28 09:16:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 15:26:47 +00:00
										 |  |  |       sig { | 
					
						
							|  |  |  |         params(measurement: Symbol, package_name: String, tap_name: String, on_request: T::Boolean, | 
					
						
							|  |  |  |                options: String).void | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       def report_influx_event(measurement, package_name:, tap_name:, on_request: false, options: "") | 
					
						
							| 
									
										
										
										
											2023-02-15 16:34:50 +00:00
										 |  |  |         return if not_this_run? || disabled? | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 15:26:47 +00:00
										 |  |  |         report_influx(measurement, package_name: package_name, tap_name: tap_name, on_request: on_request, | 
					
						
							|  |  |  | options: options) | 
					
						
							| 
									
										
										
										
											2023-02-15 16:34:50 +00:00
										 |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-07 17:24:20 -08:00
										 |  |  |       sig { params(exception: BuildError).void } | 
					
						
							| 
									
										
										
										
											2017-06-07 16:34:54 +01:00
										 |  |  |       def report_build_error(exception) | 
					
						
							| 
									
										
										
										
											2023-02-15 16:34:50 +00:00
										 |  |  |         report_google_build_error(exception) | 
					
						
							|  |  |  |         report_influx_error(exception) | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-07 17:24:20 -08:00
										 |  |  |       sig { params(exception: BuildError).void } | 
					
						
							| 
									
										
										
										
											2023-02-15 16:34:50 +00:00
										 |  |  |       def report_google_build_error(exception) | 
					
						
							|  |  |  |         return if not_this_run? || disabled? | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-07 16:34:54 +01:00
										 |  |  |         return unless exception.formula.tap | 
					
						
							| 
									
										
										
										
											2023-02-15 03:24:15 +00:00
										 |  |  |         return unless exception.formula.tap.should_report_analytics? | 
					
						
							| 
									
										
										
										
											2018-09-17 02:45:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-15 16:34:50 +00:00
										 |  |  |         formula_full_name = exception.formula.full_name | 
					
						
							|  |  |  |         package_and_options = if (options = exception.options.to_a.map(&:to_s).join(" ").presence) | 
					
						
							|  |  |  |           "#{formula_full_name} #{options}".strip | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |           formula_full_name | 
					
						
							| 
									
										
										
										
											2017-06-09 14:53:01 +03:00
										 |  |  |         end | 
					
						
							| 
									
										
										
										
											2023-03-07 17:24:20 -08:00
										 |  |  |         report_google_event(:BuildError, package_and_options) | 
					
						
							| 
									
										
										
										
											2023-02-15 16:34:50 +00:00
										 |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-07 17:24:20 -08:00
										 |  |  |       sig { params(exception: BuildError).void } | 
					
						
							| 
									
										
										
										
											2023-02-15 16:34:50 +00:00
										 |  |  |       def report_influx_error(exception) | 
					
						
							|  |  |  |         return if not_this_run? || disabled? | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 15:26:47 +00:00
										 |  |  |         formula = exception.formula | 
					
						
							|  |  |  |         return unless formula | 
					
						
							| 
									
										
										
										
											2023-02-15 16:34:50 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 15:26:47 +00:00
										 |  |  |         tap = formula.tap | 
					
						
							|  |  |  |         return unless tap | 
					
						
							|  |  |  |         return unless tap.should_report_analytics? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         options = exception.options.to_a.map(&:to_s).join(" ") | 
					
						
							|  |  |  |         report_influx_event(:build_error, package_name: formula.name, tap_name: tap.name, options: options) | 
					
						
							| 
									
										
										
										
											2016-05-03 14:21:08 +01:00
										 |  |  |       end | 
					
						
							| 
									
										
										
										
											2019-11-22 09:08:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |       def messages_displayed? | 
					
						
							|  |  |  |         config_true?(:analyticsmessage) && config_true?(:caskanalyticsmessage) | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def disabled? | 
					
						
							| 
									
										
										
										
											2020-04-05 15:44:50 +01:00
										 |  |  |         return true if Homebrew::EnvConfig.no_analytics? | 
					
						
							| 
									
										
										
										
											2019-11-22 09:08:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         config_true?(:analyticsdisabled) | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-22 17:13:51 +00:00
										 |  |  |       def not_this_run? | 
					
						
							|  |  |  |         ENV["HOMEBREW_NO_ANALYTICS_THIS_RUN"].present? | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-22 09:08:31 +00:00
										 |  |  |       def no_message_output? | 
					
						
							|  |  |  |         # Used by Homebrew/install | 
					
						
							|  |  |  |         ENV["HOMEBREW_NO_ANALYTICS_MESSAGE_OUTPUT"].present? | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def messages_displayed! | 
					
						
							| 
									
										
										
										
											2021-01-13 11:16:09 -05:00
										 |  |  |         Homebrew::Settings.write :analyticsmessage, true | 
					
						
							|  |  |  |         Homebrew::Settings.write :caskanalyticsmessage, true | 
					
						
							| 
									
										
										
										
											2019-11-22 09:08:31 +00:00
										 |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def enable! | 
					
						
							| 
									
										
										
										
											2021-01-13 11:16:09 -05:00
										 |  |  |         Homebrew::Settings.write :analyticsdisabled, false | 
					
						
							| 
									
										
										
										
											2023-02-20 09:05:15 +00:00
										 |  |  |         delete_uuid! | 
					
						
							| 
									
										
										
										
											2019-11-22 09:08:31 +00:00
										 |  |  |         messages_displayed! | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def disable! | 
					
						
							| 
									
										
										
										
											2021-01-13 11:16:09 -05:00
										 |  |  |         Homebrew::Settings.write :analyticsdisabled, true | 
					
						
							| 
									
										
										
										
											2023-02-20 09:05:15 +00:00
										 |  |  |         delete_uuid! | 
					
						
							| 
									
										
										
										
											2019-11-22 09:08:31 +00:00
										 |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-20 09:05:15 +00:00
										 |  |  |       def delete_uuid! | 
					
						
							| 
									
										
										
										
											2021-01-13 11:16:09 -05:00
										 |  |  |         Homebrew::Settings.delete :analyticsuuid | 
					
						
							| 
									
										
										
										
											2019-11-22 09:08:31 +00:00
										 |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-01 11:21:45 +01:00
										 |  |  |       def output(args:, filter: nil) | 
					
						
							| 
									
										
										
										
											2020-07-24 23:16:30 +02:00
										 |  |  |         days = args.days || "30" | 
					
						
							|  |  |  |         category = args.category || "install" | 
					
						
							| 
									
										
										
										
											2021-08-06 02:30:44 -04:00
										 |  |  |         begin | 
					
						
							|  |  |  |           json = Homebrew::API::Analytics.fetch category, days | 
					
						
							|  |  |  |         rescue ArgumentError | 
					
						
							|  |  |  |           # Ignore failed API requests | 
					
						
							|  |  |  |           return | 
					
						
							|  |  |  |         end | 
					
						
							| 
									
										
										
										
											2019-11-22 09:08:31 +00:00
										 |  |  |         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 | 
					
						
							| 
									
										
										
										
											2020-09-01 14:05:52 +01:00
										 |  |  |           next if filter.present? && key != filter && !key.start_with?("#{filter} ") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-22 09:08:31 +00:00
										 |  |  |           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 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-24 23:16:30 +02:00
										 |  |  |       def get_analytics(json, args:) | 
					
						
							| 
									
										
										
										
											2020-08-02 14:32:31 +02:00
										 |  |  |         full_analytics = args.analytics? || verbose? | 
					
						
							| 
									
										
										
										
											2019-11-22 09:08:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         ohai "Analytics" | 
					
						
							|  |  |  |         json["analytics"].each do |category, value| | 
					
						
							|  |  |  |           category = category.tr("_", "-") | 
					
						
							|  |  |  |           analytics = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           value.each do |days, results| | 
					
						
							|  |  |  |             days = days.to_i | 
					
						
							|  |  |  |             if full_analytics | 
					
						
							| 
									
										
										
										
											2020-09-01 14:05:52 +01:00
										 |  |  |               next if args.days.present? && args.days&.to_i != days | 
					
						
							|  |  |  |               next if args.category.present? && args.category != category | 
					
						
							| 
									
										
										
										
											2019-11-22 09:08:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-22 10:39:16 +00:00
										 |  |  |               table_output(category, days, results) | 
					
						
							| 
									
										
										
										
											2019-11-22 09:08:31 +00:00
										 |  |  |             else | 
					
						
							|  |  |  |               total_count = results.values.inject("+") | 
					
						
							|  |  |  |               analytics << "#{number_readable(total_count)} (#{days} days)" | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           puts "#{category}: #{analytics.join(", ")}" unless full_analytics | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-10 23:46:07 +00:00
										 |  |  |       def formula_output(formula, args:) | 
					
						
							| 
									
										
										
										
											2021-08-06 02:30:44 -04:00
										 |  |  |         return if Homebrew::EnvConfig.no_analytics? || Homebrew::EnvConfig.no_github_api? | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-10 23:46:07 +00:00
										 |  |  |         json = Homebrew::API::Formula.fetch formula.name | 
					
						
							| 
									
										
										
										
											2020-05-17 01:38:11 +05:30
										 |  |  |         return if json.blank? || json["analytics"].blank? | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-24 23:16:30 +02:00
										 |  |  |         get_analytics(json, args: args) | 
					
						
							| 
									
										
										
										
											2021-08-06 02:30:44 -04:00
										 |  |  |       rescue ArgumentError | 
					
						
							|  |  |  |         # Ignore failed API requests | 
					
						
							|  |  |  |         nil | 
					
						
							| 
									
										
										
										
											2020-05-17 01:38:11 +05:30
										 |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-24 23:16:30 +02:00
										 |  |  |       def cask_output(cask, args:) | 
					
						
							| 
									
										
										
										
											2021-08-06 02:30:44 -04:00
										 |  |  |         return if Homebrew::EnvConfig.no_analytics? || Homebrew::EnvConfig.no_github_api? | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-09 18:39:49 -04:00
										 |  |  |         json = Homebrew::API::Cask.fetch cask.token | 
					
						
							| 
									
										
										
										
											2020-05-17 01:38:11 +05:30
										 |  |  |         return if json.blank? || json["analytics"].blank? | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-24 23:16:30 +02:00
										 |  |  |         get_analytics(json, args: args) | 
					
						
							| 
									
										
										
										
											2021-08-06 02:30:44 -04:00
										 |  |  |       rescue ArgumentError | 
					
						
							|  |  |  |         # Ignore failed API requests | 
					
						
							|  |  |  |         nil | 
					
						
							| 
									
										
										
										
											2020-05-17 01:38:11 +05:30
										 |  |  |       end | 
					
						
							| 
									
										
										
										
											2019-11-22 09:08:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-15 16:34:50 +00:00
										 |  |  |       sig { returns(String) } | 
					
						
							|  |  |  |       def custom_prefix_label_google | 
					
						
							| 
									
										
										
										
											2019-11-22 09:08:31 +00:00
										 |  |  |         "custom-prefix" | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2023-02-15 16:34:50 +00:00
										 |  |  |       alias generic_custom_prefix_label_google custom_prefix_label_google | 
					
						
							| 
									
										
										
										
											2019-11-22 09:08:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-15 16:34:50 +00:00
										 |  |  |       sig { returns(String) } | 
					
						
							|  |  |  |       def arch_label_google | 
					
						
							| 
									
										
										
										
											2021-01-07 09:12:11 +00:00
										 |  |  |         if Hardware::CPU.arm? | 
					
						
							|  |  |  |           "ARM" | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |           "" | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2023-02-15 16:34:50 +00:00
										 |  |  |       alias generic_arch_label_google arch_label_google | 
					
						
							| 
									
										
										
										
											2023-02-10 16:00:51 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 15:26:47 +00:00
										 |  |  |       def clear_cache | 
					
						
							| 
									
										
										
										
											2023-02-15 16:34:50 +00:00
										 |  |  |         remove_instance_variable(:@label_google) if instance_variable_defined?(:@label_google) | 
					
						
							| 
									
										
										
										
											2023-03-20 15:26:47 +00:00
										 |  |  |         remove_instance_variable(:@default_tags_influx) if instance_variable_defined?(:@default_tags_influx) | 
					
						
							|  |  |  |         remove_instance_variable(:@default_fields_influx) if instance_variable_defined?(:@default_fields_influx) | 
					
						
							| 
									
										
										
										
											2023-02-10 16:00:51 +01:00
										 |  |  |       end | 
					
						
							| 
									
										
										
										
											2021-01-07 09:12:11 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-15 16:34:50 +00:00
										 |  |  |       sig { returns(String) } | 
					
						
							|  |  |  |       def label_google | 
					
						
							|  |  |  |         @label_google ||= begin | 
					
						
							|  |  |  |           os = OS_VERSION | 
					
						
							|  |  |  |           arch = ", #{arch_label_google}" if arch_label_google.present? | 
					
						
							|  |  |  |           prefix = ", #{custom_prefix_label_google}" unless Homebrew.default_prefix? | 
					
						
							|  |  |  |           ci = ", CI" if ENV["CI"] | 
					
						
							|  |  |  |           "#{os}#{arch}#{prefix}#{ci}" | 
					
						
							|  |  |  |         end | 
					
						
							| 
									
										
										
										
											2019-11-22 09:08:31 +00:00
										 |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-15 16:34:50 +00:00
										 |  |  |       sig { returns(T::Hash[Symbol, String]) } | 
					
						
							| 
									
										
										
										
											2023-03-20 15:26:47 +00:00
										 |  |  |       def default_tags_influx | 
					
						
							|  |  |  |         @default_tags_influx ||= begin | 
					
						
							|  |  |  |           # Only display default prefixes to reduce cardinality and improve privacy | 
					
						
							|  |  |  |           prefix = Homebrew.default_prefix? ? HOMEBREW_PREFIX.to_s : "custom-prefix" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           # Tags are always strings and must have low cardinality. | 
					
						
							|  |  |  |           { | 
					
						
							|  |  |  |             ci:             ENV["CI"].present?, | 
					
						
							|  |  |  |             prefix:         prefix, | 
					
						
							|  |  |  |             default_prefix: Homebrew.default_prefix?, | 
					
						
							|  |  |  |             developer:      Homebrew::EnvConfig.developer?, | 
					
						
							|  |  |  |             devcmdrun:      config_true?(:devcmdrun), | 
					
						
							|  |  |  |             arch:           HOMEBREW_PHYSICAL_PROCESSOR, | 
					
						
							|  |  |  |             os:             HOMEBREW_SYSTEM, | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       # remove os_version starting with " or number | 
					
						
							|  |  |  |       # remove macOS patch release | 
					
						
							|  |  |  |       sig { returns(T::Hash[Symbol, String]) } | 
					
						
							|  |  |  |       def default_fields_influx | 
					
						
							|  |  |  |         @default_fields_influx ||= begin | 
					
						
							| 
									
										
										
										
											2023-02-15 16:34:50 +00:00
										 |  |  |           version = HOMEBREW_VERSION.match(/^[\d.]+/)[0] | 
					
						
							|  |  |  |           version = "#{version}-dev" if HOMEBREW_VERSION.include?("-") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 15:26:47 +00:00
										 |  |  |           # Only include OS versions with an actual name. | 
					
						
							|  |  |  |           os_name_and_version = if (os_version = OS_VERSION.presence) && os_version.downcase.match?(/^[a-z]/) | 
					
						
							|  |  |  |             os_version | 
					
						
							|  |  |  |           end | 
					
						
							| 
									
										
										
										
											2023-02-16 13:15:04 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-15 16:34:50 +00:00
										 |  |  |           { | 
					
						
							|  |  |  |             version:             version, | 
					
						
							| 
									
										
										
										
											2023-03-20 15:26:47 +00:00
										 |  |  |             os_name_and_version: os_name_and_version, | 
					
						
							| 
									
										
										
										
											2022-05-31 18:03:48 +02:00
										 |  |  |           } | 
					
						
							| 
									
										
										
										
											2019-11-22 09:08:31 +00:00
										 |  |  |         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 | 
					
						
							| 
									
										
										
										
											2022-06-28 10:09:59 +01:00
										 |  |  |         puts "#{formatted_index_header} | #{formatted_name_with_options_header} | " \ | 
					
						
							| 
									
										
										
										
											2021-07-06 23:44:09 +05:30
										 |  |  |              "#{formatted_count_header} |  #{formatted_percent_header}" | 
					
						
							| 
									
										
										
										
											2019-11-22 09:08:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-28 10:09:59 +01:00
										 |  |  |         columns_line = "#{"-"*index_width}:|-#{"-"*name_with_options_width}-|-" \ | 
					
						
							| 
									
										
										
										
											2021-07-06 23:44:09 +05:30
										 |  |  |                        "#{"-"*count_width}:|-#{"-"*percent_width}:" | 
					
						
							| 
									
										
										
										
											2019-11-22 09:08:31 +00:00
										 |  |  |         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} | " \ | 
					
						
							| 
									
										
										
										
											2021-07-06 23:44:09 +05:30
										 |  |  |                "#{formatted_count} | #{formatted_percent}%" | 
					
						
							| 
									
										
										
										
											2019-11-22 09:08:31 +00:00
										 |  |  |           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 | 
					
						
							| 
									
										
										
										
											2022-06-28 10:09:59 +01:00
										 |  |  |         puts "#{formatted_total_footer} | #{formatted_blank_footer} | " \ | 
					
						
							| 
									
										
										
										
											2021-07-06 23:44:09 +05:30
										 |  |  |              "#{formatted_total_count_footer} | #{formatted_total_percent_footer}%" | 
					
						
							| 
									
										
										
										
											2019-11-22 09:08:31 +00:00
										 |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def config_true?(key) | 
					
						
							| 
									
										
										
										
											2021-01-13 11:16:09 -05:00
										 |  |  |         Homebrew::Settings.read(key) == "true" | 
					
						
							| 
									
										
										
										
											2019-11-22 09:08:31 +00:00
										 |  |  |       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 | 
					
						
							| 
									
										
										
										
											2016-05-03 14:21:08 +01:00
										 |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							| 
									
										
										
										
											2016-03-28 09:16:40 +01:00
										 |  |  | end | 
					
						
							| 
									
										
										
										
											2018-09-06 16:58:17 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-22 09:08:31 +00:00
										 |  |  | require "extend/os/utils/analytics" |