Skip to content

Commit

Permalink
Merge pull request #668 from rodjek/pdk-1342
Browse files Browse the repository at this point in the history
(PDK-1342) Submit PDK analytics events
  • Loading branch information
rodjek committed Jun 5, 2019
2 parents 6a6a139 + b83d748 commit 48bbbf1
Show file tree
Hide file tree
Showing 29 changed files with 939 additions and 40 deletions.
15 changes: 8 additions & 7 deletions lib/pdk.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@
module PDK
def self.analytics
@analytics ||= PDK::Analytics.build_client(
logger: PDK.logger,
disabled: ENV['PDK_DISABLE_ANALYTICS'] || PDK.config.user['analytics']['disabled'],
user_id: PDK.config.user['analytics']['user-id'],
app_id: "UA-139917834-#{PDK::Util.development_mode? ? '2' : '1'}",
client: :google_analytics,
app_name: 'pdk',
app_version: PDK::VERSION,
logger: PDK.logger,
disabled: ENV['PDK_DISABLE_ANALYTICS'] || PDK.config.user['analytics']['disabled'],
user_id: PDK.config.user['analytics']['user-id'],
app_id: "UA-139917834-#{PDK::Util.development_mode? ? '2' : '1'}",
client: :google_analytics,
app_name: 'pdk',
app_version: PDK::VERSION,
app_installer: PDK::Util.package_install? ? 'package' : 'gem',
)
end
end
23 changes: 15 additions & 8 deletions lib/pdk/analytics/client/google_analytics.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@ class GoogleAnalytics
CUSTOM_DIMENSIONS = {
operating_system: :cd1,
output_format: :cd2,
ruby_version: :cd3,
cli_options: :cd4,
env_vars: :cd5,
}.freeze

attr_reader :user_id
attr_reader :logger
attr_reader :app_name
attr_reader :app_id
attr_reader :app_version
attr_reader :app_installer

def initialize(opts)
# lazy-load expensive gem code
Expand All @@ -30,6 +34,7 @@ def initialize(opts)
@app_name = opts[:app_name]
@app_id = opts[:app_id]
@app_version = opts[:app_version]
@app_installer = opts[:app_installer]
end

def screen_view(screen, **kwargs)
Expand Down Expand Up @@ -83,21 +88,23 @@ def submit(params)
# https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters
def base_params
{
v: PROTOCOL_VERSION,
v: PROTOCOL_VERSION,
# Client ID
cid: user_id,
cid: user_id,
# Tracking ID
tid: app_id,
tid: app_id,
# Application Name
an: app_name,
an: app_name,
# Application Version
av: app_version,
av: app_version,
# Application Installer ID
aiid: app_installer,
# Anonymize IPs
aip: true,
aip: true,
# User locale
ul: Locale.current.to_rfc,
ul: Locale.current.to_rfc,
# Custom Dimension 1 (Operating System)
cd1: @os.value,
cd1: @os.value,
}
end

Expand Down
36 changes: 36 additions & 0 deletions lib/pdk/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,44 @@
require 'pdk/util/version'
require 'pdk/util/puppet_version'

class Cri::Command::CriExitException
def initialize(is_error:)
@is_error = is_error
PDK.analytics.event('CLI', 'invalid command', label: PDK::CLI.anonymised_args.join(' ')) if error?
end
end

module PDK::CLI
# Attempt to anonymise the raw ARGV array if the command parsing failed.
#
# If an item does not start with '-' but is preceeded by an item that does
# start with '-', assume that these items are an option/value pair and redact
# the value. Any additional values that do not start with '-' that follow an
# option/value pair are assumed to be arguments (rather than subcommand
# names) and are also redacted.
#
# @example
# # Where PDK::CLI.args => ['new', 'plan', '--some', 'value', 'plan_name']
#
# PDK::CLI.anonymised_args
# => ['new', 'plan', '--some', 'redacted', 'redacted']
#
# @return Array[String] the command arguments with any identifying values
# redacted.
def self.anonymised_args
in_args = false
@args.map do |arg|
if arg.start_with?('-')
in_args = true
arg
else
in_args ? 'redacted' : arg
end
end
end

def self.run(args)
@args = args
PDK::Config.analytics_config_interview! unless PDK::Config.analytics_config_exist?
@base_cmd.run(args)
rescue PDK::CLI::ExitWithError => e
Expand Down
2 changes: 2 additions & 0 deletions lib/pdk/cli/build.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ module PDK::CLI
log_level: :info,
)

PDK::CLI::Util.analytics_screen_view('build', opts)

module_metadata = PDK::Module::Metadata.from_file('metadata.json')

# TODO: Ensure forge metadata has been set, or call out to interview
Expand Down
2 changes: 2 additions & 0 deletions lib/pdk/cli/bundle.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ module PDK::CLI

PDK::CLI::Util.validate_puppet_version_opts({})

PDK::CLI::Util.analytics_screen_view('bundle')

# Ensure that the correct Ruby is activated before running commend.
puppet_env = PDK::CLI::Util.puppet_from_opts_or_env({})
PDK::Util::RubyVersion.use(puppet_env[:ruby_version])
Expand Down
2 changes: 2 additions & 0 deletions lib/pdk/cli/convert.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ module PDK::CLI
raise PDK::CLI::ExitWithError, _('You can not specify --noop and --force when converting a module')
end

PDK::CLI::Util.analytics_screen_view('convert', opts)

if opts[:'skip-interview'] && opts[:'full-interview']
PDK.logger.info _('Ignoring --full-interview and continuing with --skip-interview.')
opts[:'full-interview'] = false
Expand Down
2 changes: 2 additions & 0 deletions lib/pdk/cli/new/class.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ module PDK::CLI
raise PDK::CLI::ExitWithError, _("'%{name}' is not a valid class name") % { name: class_name }
end

PDK::CLI::Util.analytics_screen_view('new_class', opts)

PDK::Generate::PuppetClass.new(module_dir, class_name, opts).run
end
end
Expand Down
2 changes: 2 additions & 0 deletions lib/pdk/cli/new/defined_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ module PDK::CLI
raise PDK::CLI::ExitWithError, _("'%{name}' is not a valid defined type name") % { name: defined_type_name }
end

PDK::CLI::Util.analytics_screen_view('new_defined_type', opts)

PDK::Generate::DefinedType.new(module_dir, defined_type_name, opts).run
end
end
Expand Down
4 changes: 2 additions & 2 deletions lib/pdk/cli/new/module.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ module PDK::CLI

PDK::CLI::Util.validate_template_opts(opts)

PDK::CLI::Util.analytics_screen_view('new_module', opts)

if opts[:'skip-interview'] && opts[:'full-interview']
PDK.logger.info _('Ignoring --full-interview and continuing with --skip-interview.')
opts[:'full-interview'] = false
Expand All @@ -37,8 +39,6 @@ module PDK::CLI
opts[:target_dir] = target_dir.nil? ? opts[:module_name] : target_dir
end

PDK.analytics.screen_view('new_module')

PDK.logger.info(_('Creating new module: %{modname}') % { modname: module_name })
PDK::Generate::Module.invoke(opts)
end
Expand Down
2 changes: 2 additions & 0 deletions lib/pdk/cli/new/provider.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ module PDK::CLI
raise PDK::CLI::ExitWithError, _("'%{name}' is not a valid provider name") % { name: provider_name }
end

PDK::CLI::Util.analytics_screen_view('new_provider', opts)

PDK::Generate::Provider.new(module_dir, provider_name, opts).run
end
end
Expand Down
2 changes: 2 additions & 0 deletions lib/pdk/cli/new/task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ module PDK::CLI
raise PDK::CLI::ExitWithError, _("'%{name}' is not a valid task name") % { name: task_name }
end

PDK::CLI::Util.analytics_screen_view('new_task', opts)

PDK::Generate::Task.new(module_dir, task_name, opts).run
end
end
Expand Down
2 changes: 2 additions & 0 deletions lib/pdk/cli/test/unit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ module PDK::CLI

PDK::CLI::Util.module_version_check

PDK::CLI::Util.analytics_screen_view('test_unit', opts)

# Ensure that the bundled gems are up to date and correct Ruby is activated before running or listing tests.
puppet_env = PDK::CLI::Util.puppet_from_opts_or_env(opts)
PDK::Util::PuppetVersion.fetch_puppet_dev if opts[:'puppet-dev']
Expand Down
2 changes: 2 additions & 0 deletions lib/pdk/cli/update.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ module PDK::CLI
raise PDK::CLI::ExitWithError, _('You can not specify --noop and --force when updating a module')
end

PDK::CLI::Util.analytics_screen_view('update', opts)

updater = PDK::Module::Update.new(opts)

updater.run
Expand Down
35 changes: 35 additions & 0 deletions lib/pdk/cli/util.rb
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,41 @@ def validate_template_opts(opts)
raise PDK::CLI::ExitWithError, _('--template-url may not be used to specify paths containing #\'s.')
end
module_function :validate_template_opts

def analytics_screen_view(screen_name, opts = {})
dimensions = {
ruby_version: RUBY_VERSION,
}

cmd_opts = opts.dup.reject do |_, v|
v.nil? || (v.respond_to?(:empty?) && v.empty?)
end

if (format_args = cmd_opts.delete(:format))
formats = PDK::CLI::Util::OptionNormalizer.report_formats(format_args)
dimensions[:output_format] = formats.map { |r| r[:method].to_s.gsub(%r{\Awrite_}, '') }.sort.uniq.join(',')
else
dimensions[:output_format] = 'default'
end

safe_opts = [:'puppet-version', :'pe-version']
redacted_opts = cmd_opts.map do |k, v|
value = if [true, false].include?(v) || safe_opts.include?(k)
v
else
'redacted'
end
"#{k}=#{value}"
end
dimensions[:cli_options] = redacted_opts.join(',') unless redacted_opts.empty?

ignored_env_vars = %w[PDK_ANALYTICS_CONFIG PDK_DISABLE_ANALYTICS]
env_vars = ENV.select { |k, _| k.start_with?('PDK_') && !ignored_env_vars.include?(k) }.map { |k, v| "#{k}=#{v}" }
dimensions[:env_vars] = env_vars.join(',') unless env_vars.empty?

PDK.analytics.screen_view(screen_name, dimensions)
end
module_function :analytics_screen_view
end
end
end
7 changes: 7 additions & 0 deletions lib/pdk/cli/validate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ module PDK::CLI
targets = []

if opts[:list]
PDK::CLI::Util.analytics_screen_view('validate', opts)
PDK.logger.info(_('Available validators: %{validator_names}') % { validator_names: validator_names.join(', ') })
exit 0
end
Expand Down Expand Up @@ -71,6 +72,12 @@ module PDK::CLI
PDK.logger.info(_('Running all available validators...'))
end

if validators == PDK::Validate.validators
PDK::CLI::Util.analytics_screen_view('validate', opts)
else
PDK::CLI::Util.analytics_screen_view(['validate', validators.map(&:name).sort].flatten.join('_'), opts)
end

# Subsequent arguments are targets.
targets.concat(args.to_a[1..-1]) if args.length > 1

Expand Down
2 changes: 1 addition & 1 deletion lib/pdk/generate/module.rb
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ def self.prepare_metadata(opts = {})
defaults['name'] = "#{opts[:username]}-#{opts[:module_name]}" unless opts[:module_name].nil?
defaults['author'] = PDK.answers['author'] unless PDK.answers['author'].nil?
defaults['license'] = PDK.answers['license'] unless PDK.answers['license'].nil?
defaults['license'] = opts[:license] if opts.key? :license
defaults['license'] = opts[:license] if opts.key?(:license)

metadata = PDK::Module::Metadata.new(defaults)
module_interview(metadata, opts) unless opts[:'skip-interview']
Expand Down
23 changes: 20 additions & 3 deletions lib/pdk/module/templatedir.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ def initialize(uri, module_metadata = {}, init = false)

@module_metadata = module_metadata

template_type = uri.default? ? 'default' : 'custom'
PDK.analytics.event('TemplateDir', 'initialize', label: template_type)

yield self
ensure
# If we cloned a git repo to get the template, remove the clone once
Expand Down Expand Up @@ -256,14 +259,28 @@ def config_for(dest_path, sync_config_path = nil)

if @config.nil?
conf_defaults = read_config(config_path)
sync_config = read_config(sync_config_path) unless sync_config_path.nil?
@sync_config = read_config(sync_config_path) unless sync_config_path.nil?
@config = conf_defaults
@config.deep_merge!(sync_config, knockout_prefix: '---') unless sync_config.nil?
@config.deep_merge!(@sync_config, knockout_prefix: '---') unless @sync_config.nil?
end
file_config = @config.fetch(:global, {})
file_config['module_metadata'] = @module_metadata
file_config.merge!(@config.fetch(dest_path, {})) unless dest_path.nil?
file_config.merge!(@config)
file_config.merge!(@config).tap do |c|
if uri.default?
file_value = if c['unmanaged']
'unmanaged'
elsif c['delete']
'deleted'
elsif @sync_config && @sync_config.key?(dest_path)
'customized'
else
'default'
end

PDK.analytics.event('TemplateDir', 'file', label: dest_path, value: file_value)
end
end
end

# Generates a hash of data from a given yaml file location.
Expand Down
2 changes: 1 addition & 1 deletion spec/spec_helper_acceptance.rb
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def execute_script(script)
puts "Working in #{tempdir}"

analytics_config = Tempfile.new('analytics.yml')
analytics_config.write(YAML.dump(disabled: true))
analytics_config.write(YAML.dump('disabled' => true))
analytics_config.close
ENV['PDK_ANALYTICS_CONFIG'] = analytics_config.path
end
Expand Down
1 change: 1 addition & 0 deletions spec/unit/pdk/analytics/client/google_analytics_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
av: options[:app_version],
cid: options[:user_id],
tid: options[:app_id],
aiid: options[:app_installer],
ul: Locale.current.to_rfc,
aip: true,
cd1: os_name,
Expand Down
Loading

0 comments on commit 48bbbf1

Please sign in to comment.