inspec-core 3.5.0 → 3.6.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/etc/deprecations.json +9 -0
  3. data/lib/bundles/inspec-supermarket/cli.rb +1 -1
  4. data/lib/inspec/backend.rb +9 -8
  5. data/lib/inspec/base_cli.rb +12 -170
  6. data/lib/inspec/cli.rb +17 -16
  7. data/lib/inspec/config.rb +391 -0
  8. data/lib/inspec/control_eval_context.rb +2 -1
  9. data/lib/inspec/dsl.rb +2 -2
  10. data/lib/inspec/errors.rb +5 -0
  11. data/lib/inspec/plugin/v1/plugin_types/resource.rb +1 -1
  12. data/lib/inspec/plugin/v2/activator.rb +24 -3
  13. data/lib/inspec/plugin/v2/loader.rb +1 -1
  14. data/lib/inspec/plugin/v2/registry.rb +8 -12
  15. data/lib/inspec/profile.rb +3 -2
  16. data/lib/inspec/profile_vendor.rb +2 -1
  17. data/lib/inspec/rspec_extensions.rb +2 -2
  18. data/lib/inspec/runner.rb +5 -5
  19. data/lib/inspec/shell.rb +1 -1
  20. data/lib/inspec/ui.rb +2 -2
  21. data/lib/inspec/version.rb +1 -1
  22. data/lib/plugins/inspec-habitat/lib/inspec-habitat/profile.rb +1 -1
  23. data/lib/plugins/inspec-init/lib/inspec-init/cli.rb +3 -28
  24. data/lib/plugins/inspec-init/lib/inspec-init/cli_plugin.rb +245 -0
  25. data/lib/plugins/inspec-init/lib/inspec-init/cli_profile.rb +49 -0
  26. data/lib/plugins/inspec-init/lib/inspec-init/renderer.rb +43 -31
  27. data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/Gemfile +12 -0
  28. data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/LICENSE +2 -0
  29. data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/README.md +28 -0
  30. data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/Rakefile +40 -0
  31. data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/inspec-plugin-template.gemspec +45 -0
  32. data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template.rb +16 -0
  33. data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template/cli_command.rb +64 -0
  34. data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template/plugin.rb +55 -0
  35. data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template/version.rb +10 -0
  36. data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/test/fixtures/README.md +24 -0
  37. data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/test/functional/README.md +12 -0
  38. data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/test/functional/inspec_plugin_template_test.rb +110 -0
  39. data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/test/helper.rb +26 -0
  40. data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/test/unit/README.md +17 -0
  41. data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/test/unit/cli_args_test.rb +67 -0
  42. data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/test/unit/plugin_def_test.rb +51 -0
  43. data/lib/plugins/inspec-init/{lib/inspec-init/templates → templates}/profiles/os/README.md +0 -0
  44. data/lib/plugins/inspec-init/{lib/inspec-init/templates → templates}/profiles/os/controls/example.rb +0 -0
  45. data/lib/plugins/inspec-init/{lib/inspec-init/templates → templates}/profiles/os/inspec.yml +0 -0
  46. data/lib/plugins/inspec-init/{lib/inspec-init/templates → templates}/profiles/os/libraries/.gitkeep +0 -0
  47. data/lib/plugins/inspec-init/test/functional/inspec_init_plugin_test.rb +173 -0
  48. data/lib/plugins/inspec-init/test/functional/{inspec_init_test.rb → inspec_init_profile_test.rb} +7 -7
  49. data/lib/resources/filesystem.rb +40 -12
  50. metadata +31 -11
@@ -40,7 +40,8 @@ module Inspec
40
40
  if hook
41
41
  # OK, load the hook if it hasn't been already. We'll then know a module,
42
42
  # which we can then inject into the context
43
- registry.activate(:control_dsl, method_name) unless hook.activated?
43
+ hook.activate
44
+
44
45
  # Inject the module's methods into the context.
45
46
  # implementation_class is the field name, but this is actually a module.
46
47
  self.class.include(hook.implementation_class)
data/lib/inspec/dsl.rb CHANGED
@@ -38,7 +38,7 @@ module Inspec::DSL
38
38
  if hook
39
39
  # OK, load the hook if it hasn't been already. We'll then know a module,
40
40
  # which we can then inject into the context
41
- registry.activate(:outer_profile_dsl, method_name) unless hook.activated?
41
+ hook.activate
42
42
  # Inject the module's methods into the context
43
43
  # implementation_class is the field name, but this is actually a module.
44
44
  self.class.include(hook.implementation_class)
@@ -78,7 +78,7 @@ module Inspec::DSL
78
78
  end
79
79
 
80
80
  def self.filter_included_controls(context, profile, &block)
81
- mock = Inspec::Backend.create({ backend: 'mock' })
81
+ mock = Inspec::Backend.create(Inspec::Config.mock)
82
82
  include_ctx = Inspec::ProfileContext.for_profile(profile, mock, {})
83
83
  include_ctx.load(block) if block_given?
84
84
  # remove all rules that were not registered
data/lib/inspec/errors.rb CHANGED
@@ -13,6 +13,11 @@ module Inspec
13
13
  class ReporterError < Error; end
14
14
  class ImpactError < Error; end
15
15
 
16
+ # Config file loading
17
+ class ConfigError < Error; end
18
+ class ConfigError::MalformedJson < ConfigError; end
19
+ class ConfigError::Invalid < ConfigError; end
20
+
16
21
  class Attribute
17
22
  class Error < Inspec::Error; end
18
23
  class ValidationError < Error
@@ -52,7 +52,7 @@ module Inspec
52
52
  if hook
53
53
  # OK, load the hook if it hasn't been already. We'll then know a module,
54
54
  # which we can then inject into the resource
55
- registry.activate(:resource_dsl, method_name) unless hook.activated?
55
+ hook.activate
56
56
  # Inject the module's methods into the resource as class methods.
57
57
  # implementation_class is the field name, but this is actually a module.
58
58
  extend(hook.implementation_class)
@@ -3,7 +3,7 @@ module Inspec::Plugin::V2
3
3
  :plugin_name,
4
4
  :plugin_type,
5
5
  :activator_name,
6
- :'activated?',
6
+ :activated?,
7
7
  :exception,
8
8
  :activation_proc,
9
9
  :implementation_class,
@@ -14,8 +14,29 @@ module Inspec::Plugin::V2
14
14
  end
15
15
 
16
16
  def activated?(new_value = nil)
17
- return self[:'activated?'] if new_value.nil?
18
- self[:'activated?'] = new_value
17
+ return self[:activated?] if new_value.nil?
18
+ self[:activated?] = new_value
19
+ end
20
+
21
+ # Load a plugin, but if an error is encountered, store it and continue
22
+ def activate
23
+ return if activated?
24
+ # rubocop: disable Lint/RescueException
25
+ begin
26
+ impl_class = self[:activation_proc].call
27
+ self[:activated?] = true
28
+ self[:implementation_class] = impl_class
29
+ rescue Exception => ex
30
+ self[:exception] = ex
31
+ Inspec::Log.error "Could not activate #{self[:plugin_type]} hook named '#{self[:activator_name]}' for plugin #{self[:plugin_name]}"
32
+ end
33
+ # rubocop: enable Lint/RescueException
34
+ end
35
+
36
+ # Load a plugin, but if an error is encountered, re-throw it
37
+ def activate!
38
+ activate
39
+ raise exception if exception
19
40
  end
20
41
  end
21
42
  end
@@ -102,7 +102,7 @@ module Inspec::Plugin::V2
102
102
 
103
103
  # OK, activate.
104
104
  if activate_me
105
- registry.activate(:cli_command, act.activator_name)
105
+ act.activate
106
106
  act.implementation_class.register_with_thor
107
107
  end
108
108
  end
@@ -67,19 +67,15 @@ module Inspec::Plugin::V2
67
67
  end
68
68
  end
69
69
 
70
- def activate(plugin_type, hook_name)
71
- activator = find_activators(plugin_type: plugin_type, activator_name: hook_name).first
72
- # We want to capture literally any possible exception here, since we are storing them.
73
- # rubocop: disable Lint/RescueException
74
- begin
75
- impl_class = activator.activation_proc.call
76
- activator.activated?(true)
77
- activator.implementation_class = impl_class
78
- rescue Exception => ex
79
- activator.exception = ex
80
- Inspec::Log.error "Could not activate #{activator.plugin_type} hook named '#{activator.activator_name}' for plugin #{plugin_name}"
70
+ # Convenience method for when you expect exactly one
71
+ def find_activator(filters = {})
72
+ matched_plugins = find_activators(filters)
73
+ if matched_plugins.count > 1
74
+ raise Inspec::Plugin::V2::LoadError, "Plugin hooks search returned multiple results for filter #{filters.inspect} - use more filters, or use find_activators (plural)"
75
+ elsif matched_plugins.empty?
76
+ raise Inspec::Plugin::V2::LoadError, "Plugin hooks search returned zero results for filter #{filters.inspect}"
81
77
  end
82
- # rubocop: enable Lint/RescueException
78
+ matched_plugins.first
83
79
  end
84
80
 
85
81
  def register(name, status)
@@ -67,7 +67,8 @@ module Inspec
67
67
  new(reader, opts)
68
68
  end
69
69
 
70
- def self.for_fetcher(fetcher, opts)
70
+ def self.for_fetcher(fetcher, config)
71
+ opts = config.respond_to?(:final_options) ? config.final_options : config
71
72
  opts[:vendor_cache] = opts[:vendor_cache] || Cache.new
72
73
  path, writable = fetcher.fetch
73
74
  for_path(path, opts.merge(target: fetcher.target, writable: writable))
@@ -113,7 +114,7 @@ module Inspec
113
114
  #
114
115
  # This will cause issues if a profile attempts to load a file via `inspec.profile.file`
115
116
  train_options = options.reject { |k, _| k == 'target' } # See https://github.com/chef/inspec/pull/1646
116
- @backend = options[:backend].nil? ? Inspec::Backend.create(train_options) : options[:backend].dup
117
+ @backend = options[:backend].nil? ? Inspec::Backend.create(Inspec::Config.new(train_options)) : options[:backend].dup
117
118
  @runtime_profile = RuntimeProfile.new(self)
118
119
  @backend.profile = @runtime_profile
119
120
 
@@ -2,6 +2,7 @@
2
2
  # author: Adam Leff
3
3
 
4
4
  require 'inspec/profile'
5
+ require 'inspec/config'
5
6
 
6
7
  module Inspec
7
8
  class ProfileVendor
@@ -49,7 +50,7 @@ module Inspec
49
50
  def profile_opts
50
51
  {
51
52
  vendor_cache: Inspec::Cache.new(cache_path.to_s),
52
- backend: Inspec::Backend.create(target: 'mock://'),
53
+ backend: Inspec::Backend.create(Inspec::Config.mock),
53
54
  }
54
55
  end
55
56
 
@@ -17,7 +17,7 @@ module Inspec
17
17
  if hook
18
18
  # OK, load the hook if it hasn't been already. We'll then know a module,
19
19
  # which we can then inject into the context
20
- registry.activate(:describe_dsl, method_name) unless hook.activated?
20
+ hook.activate
21
21
 
22
22
  # Inject the module's methods into the example group contexts.
23
23
  # implementation_class is the field name, but this is actually a module.
@@ -46,7 +46,7 @@ module Inspec
46
46
  if hook
47
47
  # OK, load the hook if it hasn't been already. We'll then know a module,
48
48
  # which we can then inject into the context
49
- registry.activate(:test_dsl, method_name) unless hook.activated?
49
+ hook.activate
50
50
 
51
51
  # Inject the module's methods into the example group contexts.
52
52
  # implementation_class is the field name, but this is actually a module.
data/lib/inspec/runner.rb CHANGED
@@ -10,6 +10,7 @@ require 'inspec/profile_context'
10
10
  require 'inspec/profile'
11
11
  require 'inspec/metadata'
12
12
  require 'inspec/secrets'
13
+ require 'inspec/config'
13
14
  require 'inspec/dependencies/cache'
14
15
  # spec requirements
15
16
 
@@ -34,7 +35,10 @@ module Inspec
34
35
  attr_reader :backend, :rules, :attributes
35
36
  def initialize(conf = {})
36
37
  @rules = []
37
- @conf = conf.dup
38
+ # If we were handed a Hash config (by audit cookbook or kitchen-inspec),
39
+ # upgrade it to a proper config. This handles a lot of config finalization,
40
+ # like reporter parsing.
41
+ @conf = conf.is_a?(Hash) ? Inspec::Config.new(conf) : conf
38
42
  @conf[:logger] ||= Logger.new(nil)
39
43
  @target_profiles = []
40
44
  @controls = @conf[:controls] || []
@@ -42,10 +46,6 @@ module Inspec
42
46
  @create_lockfile = @conf[:create_lockfile]
43
47
  @cache = Inspec::Cache.new(@conf[:vendor_cache])
44
48
 
45
- # parse any ad-hoc runners reporter formats
46
- # this has to happen before we load the test_collector
47
- @conf = Inspec::BaseCLI.parse_reporters(@conf) if @conf[:type].nil?
48
-
49
49
  @test_collector = @conf.delete(:test_collector) || begin
50
50
  require 'inspec/runner_rspec'
51
51
  RunnerRspec.new(@conf)
data/lib/inspec/shell.rb CHANGED
@@ -104,7 +104,7 @@ module Inspec
104
104
  puts <<~EOF
105
105
  You are currently running on:
106
106
 
107
- #{Inspec::BaseCLI.detect(params: ctx.platform.params, indent: 4, color: 39)}
107
+ #{Inspec::BaseCLI.format_platform_info(params: ctx.platform.params, indent: 4, color: 39)}
108
108
  EOF
109
109
  end
110
110
 
data/lib/inspec/ui.rb CHANGED
@@ -61,7 +61,7 @@ module Inspec
61
61
  print_or_return(str.to_s, opts[:print])
62
62
  end
63
63
 
64
- def plain_line(str, opts = { print: true })
64
+ def plain_line(str = '', opts = { print: true })
65
65
  print_or_return(str.to_s + "\n", opts[:print])
66
66
  end
67
67
 
@@ -81,7 +81,7 @@ module Inspec
81
81
  # High-Level formatting methods
82
82
  #=========================================================================#
83
83
 
84
- def emphasis(str, opts = { print: true })
84
+ def emphasis(str, opts = { print: false })
85
85
  cyan(str, opts)
86
86
  end
87
87
 
@@ -4,5 +4,5 @@
4
4
  # author: Christoph Hartmann
5
5
 
6
6
  module Inspec
7
- VERSION = '3.5.0'
7
+ VERSION = '3.6.2'
8
8
  end
@@ -103,7 +103,7 @@ module InspecPlugins
103
103
  def create_profile_object
104
104
  @profile = Inspec::Profile.for_target(
105
105
  path,
106
- backend: Inspec::Backend.create(target: 'mock://'),
106
+ backend: Inspec::Backend.create(Inspec::Config.mock),
107
107
  )
108
108
  end
109
109
 
@@ -8,35 +8,10 @@ module InspecPlugins
8
8
  class CLI < Inspec.plugin(2, :cli_command)
9
9
  subcommand_desc 'init SUBCOMMAND', 'Generate InSpec code'
10
10
 
11
- #-------------------------------------------------------------------#
12
- # inspec init profile
13
- #-------------------------------------------------------------------#
14
- def self.valid_profile_platforms
15
- # Look in the 'template/profiles' directory and detect which platforms are available.
16
- profile_templates_dir = File.join(File.dirname(__FILE__), 'templates', 'profiles')
17
- Dir.glob(File.join(profile_templates_dir, '*')).select { |p| File.directory?(p) }.map { |d| File.basename(d) }
18
- end
11
+ TEMPLATES_PATH = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'templates'))
19
12
 
20
- no_commands do
21
- def valid_profile_platforms
22
- self.class.valid_profile_platforms
23
- end
24
- end
25
-
26
- desc 'profile [OPTIONS] NAME', 'Generate a new profile'
27
- option :platform, default: 'os', type: :string, aliases: [:p],
28
- desc: "Which platform to generate a platform for: choose from #{valid_profile_platforms.join(', ')}"
29
- option :overwrite, type: :boolean, default: false,
30
- desc: 'Overwrites existing directory'
31
- def profile(new_profile_name)
32
- unless valid_profile_platforms.include?(options[:platform])
33
- puts "Unable to generate profile: No template available for platform '#{options[:platform]}' (expected one of: #{valid_profile_platforms.join(', ')})"
34
- exit 1
35
- end
36
- template_path = File.join('profiles', options[:platform])
37
- renderer = InspecPlugins::Init::Renderer.new(self, options)
38
- renderer.render_with_values(template_path, name: new_profile_name)
39
- end
13
+ require_relative 'cli_profile'
14
+ require_relative 'cli_plugin'
40
15
  end
41
16
  end
42
17
  end
@@ -0,0 +1,245 @@
1
+ # encoding: utf-8
2
+
3
+ require_relative 'renderer'
4
+
5
+ module InspecPlugins
6
+ module Init
7
+ class CLI < Inspec.plugin(2, :cli_command)
8
+ #-------------------------------------------------------------------#
9
+ # inspec init plugin
10
+ #-------------------------------------------------------------------#
11
+ desc 'PLUGIN_NAME [options]', 'Generates an InSpec plugin, which can extend the functionality of InSpec itself.'
12
+ # General options
13
+ option :prompt, type: :boolean, default: true, desc: 'Interactively prompt for information to put in your generated plugin.'
14
+ option :detail, type: :string, default: 'full', desc: "How detailed of a plugin to generate. 'full' is a normal full gem with tests; 'core' has tests but no gemspec; 'test-fixture' is stripped down for a test fixture."
15
+
16
+ # Templating vars
17
+ option :author_email, type: :string, default: 'you@example.com', desc: 'Author Email for gemspec'
18
+ option :author_name, type: :string, default: 'Your Name', desc: 'Author Name for gemspec'
19
+ option :description, type: :string, default: '', desc: 'Multi-line description of the plugin'
20
+ option :summary, type: :string, default: 'A plugin with a default summary', desc: 'One-line summary of your plugin'
21
+ option :license_name, type: :string, default: 'Apache-2.0', desc: 'The name of a license'
22
+ option :hook, type: :array, default: ['cli_command:my_command'], desc: 'A list of plugin hooks, in the form type1:name1, type2:name2, etc'
23
+ # These vars have calculated defaults
24
+ option :homepage, type: :string, default: nil, desc: 'A URL for your project, often a GitHub link'
25
+ option :module_name, type: :string, default: nil, desc: 'Module Name for your plugin package. Will change plugin name to CamelCase by default.'
26
+ option :license_text, type: :string, default: '', hide: true
27
+ option :plugin_name, type: :string, default: '', hide: true # This is here to give a uniform interface
28
+ option :copyright, type: :string, default: nil, desc: 'A copyright statement, to be added to LICENSE'
29
+
30
+ def plugin(plugin_name)
31
+ plugin_type = determine_plugin_type(plugin_name)
32
+ snake_case = plugin_name.tr('-', '_')
33
+
34
+ template_vars = {
35
+ name: plugin_name,
36
+ plugin_name: plugin_name,
37
+ snake_case: snake_case,
38
+ }.merge(plugin_vars_from_opts)
39
+
40
+ template_path = File.join('plugins', plugin_type + '-plugin-template')
41
+
42
+ render_opts = {
43
+ templates_path: TEMPLATES_PATH,
44
+ overwrite: options[:overwrite],
45
+ file_rename_map: make_rename_map(plugin_type, plugin_name, snake_case),
46
+ skip_files: make_skip_list,
47
+ }
48
+ renderer = InspecPlugins::Init::Renderer.new(ui, render_opts)
49
+
50
+ renderer.render_with_values(template_path, plugin_type + ' plugin', template_vars)
51
+ end
52
+
53
+ private
54
+
55
+ def determine_plugin_type(plugin_name)
56
+ plugin_type = plugin_name.match(/^(inspec|train)\-/)
57
+ unless plugin_type
58
+ ui.error('Plugin names must begin with either ' + ui.emphasis('inspec') + ' or ' + ui.emphasis('train') + ' - saw ' + ui.emphasis(plugin_name))
59
+ ui.exit(:usage_error)
60
+ end
61
+ options[:plugin_name] = plugin_name
62
+
63
+ plugin_type = plugin_type[1]
64
+ unless plugin_type == 'inspec'
65
+ ui.error('Sorry, only InSpec (inspec-) plugins are supported at this time: Train (train-) support is not implemented yet.')
66
+ ui.exit(:usage_error)
67
+ end
68
+ plugin_type
69
+ end
70
+
71
+ def make_rename_map(_plugin_type, plugin_name, snake_case)
72
+ {
73
+ 'inspec-plugin-template.gemspec' => plugin_name + '.gemspec',
74
+ File.join('lib', 'inspec-plugin-template') => File.join('lib', plugin_name),
75
+ File.join('lib', 'inspec-plugin-template.rb') => File.join('lib', plugin_name + '.rb'),
76
+ File.join('lib', 'inspec-plugin-template', 'cli_command.rb') => File.join('lib', plugin_name, 'cli_command.rb'),
77
+ File.join('lib', 'inspec-plugin-template', 'plugin.rb') => File.join('lib', plugin_name, 'plugin.rb'),
78
+ File.join('lib', 'inspec-plugin-template', 'version.rb') => File.join('lib', plugin_name, 'version.rb'),
79
+ File.join('test', 'functional', 'inspec_plugin_template_test.rb') => File.join('test', 'functional', snake_case + '_test.rb'),
80
+ }
81
+ end
82
+
83
+ def plugin_vars_from_opts
84
+ # Set dynamic default - module name is straightforward. Copyright, homepage, and license_text depend on other prompted vars.
85
+ options[:module_name] ||= options[:plugin_name].sub(/^(inspec|train)\-/, '').split('-').map(&:capitalize).join('')
86
+
87
+ if options[:prompt] && ui.interactive?
88
+ vars = options.dup.merge(vars_from_prompts)
89
+ elsif !options[:prompt]
90
+ vars = options.dup.merge(vars_from_defaults)
91
+
92
+ else
93
+ ui.error('You requested interactive prompting for the template variables, but this does not seem to be an interactive terminal.')
94
+ ui.exit(:usage_error)
95
+ end
96
+ vars.merge(parse_hook_option(options[:hook]))
97
+ end
98
+
99
+ def vars_from_defaults
100
+ options[:copyright] ||= 'Copyright © ' + Date.today.year.to_s + ' ' + options[:author_name]
101
+ options[:homepage] ||= 'https://github.com/' + options[:author_email].split('@').first + '/' + options[:plugin_name]
102
+ options[:license_text] = fetch_license_text(options[:license_name])
103
+ options
104
+ end
105
+
106
+ def vars_from_prompts
107
+ order = {
108
+ author_name: {},
109
+ author_email: {},
110
+ summary: {},
111
+ description: { mode: :multiline },
112
+ module_name: {},
113
+ copyright: { default_setter: proc { options[:copyright] ||= 'Copyright © ' + Date.today.year.to_s + ' ' + options[:author_name] } },
114
+ license_name: {
115
+ mode: :select,
116
+ choices: [
117
+ { name: 'Apache 2.0', value: 'Apache-2.0', default: true },
118
+ { name: 'Modified BSD', value: 'BSD-3-Clause' },
119
+ { name: 'Proprietary (Closed Source)', value: 'Proprietary' },
120
+ { name: 'Other (edit LICENSE yourself)', value: 'Other' },
121
+ ],
122
+ },
123
+ homepage: { default_setter: proc { options[:homepage] ||= 'https://github.com/' + options[:author_email].split('@').first + '/' + options[:plugin_name] } }
124
+ # TODO: Handle hooks, when we ever have more than one type of plugin
125
+ }
126
+
127
+ prompt_for_options(order)
128
+
129
+ options[:license_text] = fetch_license_text(options[:license_name])
130
+
131
+ options
132
+ end
133
+
134
+ def prompt_for_options(option_order) # rubocop: disable Metrics/AbcSize
135
+ option_defs = self.class.all_commands['plugin'].options
136
+
137
+ option_order.each do |opt_name, prompt_options|
138
+ opt_def = option_defs[opt_name]
139
+ prompt_options[:default_setter]&.call
140
+
141
+ case prompt_options[:mode]
142
+ when :select
143
+ options[opt_name] = ui.prompt.select('Choose ' + opt_def.description + ':', prompt_options[:choices])
144
+ if opt_name == :license_name && options[opt_name] == 'Other'
145
+ ui.plain_line 'OK, be sure to update the ' + ui.emphasis('LICENSE') + ' file with your license details.'
146
+ end
147
+ when :multiline
148
+ options[opt_name] = ui.prompt.multiline('Enter ' + opt_def.description + '. Press Control-D to end.', default: options[opt_name])
149
+ else
150
+ # Assume plain ask
151
+ options[opt_name] = ui.prompt.ask('Enter ' + opt_def.description + ':', default: options[opt_name])
152
+ end
153
+ end
154
+ end
155
+
156
+ def parse_hook_option(raw_option)
157
+ hooks_by_type = {}
158
+ raw_option.each do |entry|
159
+ parts = entry.split(':')
160
+ type = parts.first.to_sym
161
+ name = parts.last
162
+ if hooks_by_type.key?(type)
163
+ ui.error 'The InSpec plugin generator can currently only generate one hook of each type'
164
+ ui.exit(:usage_error)
165
+ end
166
+ hooks_by_type[type] = name
167
+ end
168
+
169
+ vars = { hooks: hooks_by_type }
170
+ if hooks_by_type.key?(:cli_command)
171
+ vars[:command_name_dashes] = hooks_by_type[:cli_command].tr('_', '-')
172
+ vars[:command_name_snake] = hooks_by_type[:cli_command].tr('-', '_')
173
+ end
174
+ vars
175
+ end
176
+
177
+ def fetch_license_text(license_name)
178
+ case license_name
179
+ when 'Proprietary'
180
+ <<~EOL
181
+ Proprietary software. All Rights Reserved.
182
+ EOL
183
+ when 'Apache-2.0'
184
+ <<~EOL
185
+ Licensed under the Apache License, Version 2.0 (the "License");
186
+ you may not use this file except in compliance with the License.
187
+ You may obtain a copy of the License at
188
+ http://www.apache.org/licenses/LICENSE-2.0
189
+ Unless required by applicable law or agreed to in writing, software
190
+ distributed under the License is distributed on an "AS IS" BASIS,
191
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
192
+ See the License for the specific language governing permissions and
193
+ limitations under the License.
194
+
195
+ EOL
196
+ when 'BSD-3-Clause'
197
+ <<~EOL
198
+ Modified BSD License
199
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
200
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
201
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
202
+ 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
203
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
204
+ EOL
205
+ else
206
+ '"Other" license selected at plugin generation time - please insert your license here.'
207
+ end
208
+ end
209
+
210
+ def make_skip_list
211
+ case options[:detail]
212
+ when 'full'
213
+ []
214
+ when 'core'
215
+ [
216
+ 'Gemfile',
217
+ 'inspec-plugin-template.gemspec',
218
+ 'LICENSE',
219
+ 'Rakefile',
220
+ ]
221
+ when 'test-fixture'
222
+ [
223
+ 'Gemfile',
224
+ 'inspec-plugin-template.gemspec',
225
+ 'LICENSE',
226
+ 'Rakefile',
227
+ File.join('test', 'fixtures', 'README.md'),
228
+ File.join('test', 'fixtures'),
229
+ File.join('test', 'functional', 'inspec_plugin_template_test.rb'),
230
+ File.join('test', 'functional', 'README.md'),
231
+ File.join('test', 'unit', 'cli_args_test.rb'),
232
+ File.join('test', 'unit', 'plugin_def_test.rb'),
233
+ File.join('test', 'unit', 'README.md'),
234
+ File.join('test', 'unit'),
235
+ File.join('test', 'helper.rb'),
236
+ File.join('test'),
237
+ ]
238
+ else
239
+ ui.error "Unrecognized value for 'detail': #{options[:detail]} - expected one of full, core, test-fixture"
240
+ ui.exit(:usage_error)
241
+ end
242
+ end
243
+ end
244
+ end
245
+ end