pdk 0.6.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,53 @@
1
+ require 'tty-spinner'
2
+ require 'tty-which'
3
+
4
+ require 'pdk/util'
5
+
6
+ module PDK
7
+ module CLI
8
+ class ExecGroup
9
+ attr_reader :commands
10
+
11
+ def initialize(message, opts = {})
12
+ @options = opts.merge(PDK::CLI::Util.spinner_opts_for_platform)
13
+
14
+ unless PDK.logger.debug?
15
+ @multi_spinner = TTY::Spinner::Multi.new("[:spinner] #{message}", @options)
16
+ @multi_spinner.auto_spin
17
+ end
18
+
19
+ @threads = []
20
+ @exit_codes = []
21
+ end
22
+
23
+ def register
24
+ raise PDK::CLI::FatalError, 'No block registered' unless block_given?
25
+
26
+ @threads << Thread.new do
27
+ GettextSetup.initialize(File.absolute_path('../../../locales', File.dirname(__FILE__)))
28
+ GettextSetup.negotiate_locale!(GettextSetup.candidate_locales)
29
+ @exit_codes << yield
30
+ end
31
+ end
32
+
33
+ def add_spinner(message, opts = {})
34
+ return if PDK.logger.debug?
35
+ @multi_spinner.register("[:spinner] #{message}", @options.merge(opts).merge(PDK::CLI::Util.spinner_opts_for_platform))
36
+ end
37
+
38
+ def exit_code
39
+ @threads.each(&:join)
40
+
41
+ exit_code = @exit_codes.max
42
+
43
+ if exit_code.zero? && @multi_spinner
44
+ @multi_spinner.success
45
+ elsif @multi_spinner
46
+ @multi_spinner.error
47
+ end
48
+
49
+ exit_code
50
+ end
51
+ end
52
+ end
53
+ end
@@ -1,7 +1,7 @@
1
1
  module PDK::CLI
2
2
  @new_class_cmd = @new_cmd.define_command do
3
3
  name 'class'
4
- usage _('class [options] <class_name> [parameter[:type]] [parameter[:type]] ...')
4
+ usage _('class [options] <class_name>')
5
5
  summary _('Create a new class named <class_name> using given options')
6
6
 
7
7
  PDK::CLI.template_url_option(self)
@@ -23,10 +23,6 @@ module PDK::CLI
23
23
  raise PDK::CLI::FatalError, _("'%{name}' is not a valid class name") % { name: class_name }
24
24
  end
25
25
 
26
- if args.length > 1
27
- opts[:params] = args[1..-1].map { |r| Util::OptionNormalizer.parameter_specification(r) }
28
- end
29
-
30
26
  PDK::Generate::PuppetClass.new(module_dir, class_name, opts).run
31
27
  end
32
28
  end
@@ -10,6 +10,17 @@ module PDK
10
10
  raise PDK::CLI::FatalError, message if PDK::Util.module_root.nil?
11
11
  end
12
12
  module_function :ensure_in_module!
13
+
14
+ def spinner_opts_for_platform
15
+ windows_opts = {
16
+ success_mark: '*',
17
+ error_mark: 'X',
18
+ }
19
+
20
+ return windows_opts if Gem.win_platform?
21
+ {}
22
+ end
23
+ module_function :spinner_opts_for_platform
13
24
  end
14
25
  end
15
26
  end
@@ -1,4 +1,5 @@
1
1
  require 'tty-prompt'
2
+ require 'pdk/monkey_patches'
2
3
 
3
4
  module PDK
4
5
  module CLI
@@ -15,6 +15,7 @@ module PDK::CLI
15
15
 
16
16
  flag nil, :list, _('list all available validators')
17
17
  flag :a, 'auto-correct', _('automatically correct problems (where possible)')
18
+ flag nil, :parallel, _('run validations in parallel')
18
19
 
19
20
  run do |opts, args, _cmd|
20
21
  if args == ['help']
@@ -76,14 +77,25 @@ module PDK::CLI
76
77
  options = targets.empty? ? {} : { targets: targets }
77
78
  options[:auto_correct] = true if opts.key?(:'auto-correct')
78
79
 
79
- exit_code = 0
80
-
81
80
  # Ensure that the bundle is installed and tools are available before running any validations.
82
81
  PDK::Util::Bundler.ensure_bundle!
83
82
 
84
- validators.each do |validator|
85
- validator_exit_code = validator.invoke(report, options)
86
- exit_code = validator_exit_code if validator_exit_code != 0
83
+ exit_code = 0
84
+ if opts[:parallel]
85
+ exec_group = PDK::CLI::ExecGroup.new(_('Validating module using %{num_of_threads} threads' % { num_of_threads: validators.count }), opts)
86
+
87
+ validators.each do |validator|
88
+ exec_group.register do
89
+ validator.invoke(report, options.merge(exec_group: exec_group))
90
+ end
91
+ end
92
+
93
+ exit_code = exec_group.exit_code
94
+ else
95
+ validators.each do |validator|
96
+ validator_exit_code = validator.invoke(report, options.dup)
97
+ exit_code = validator_exit_code if validator_exit_code != 0
98
+ end
87
99
  end
88
100
 
89
101
  report_formats.each do |format|
@@ -15,7 +15,15 @@ require 'pdk/util/version'
15
15
  module PDK
16
16
  module Generate
17
17
  class Module
18
- DEFAULT_TEMPLATE = 'https://github.com/puppetlabs/pdk-module-template'.freeze
18
+ def self.default_template_url
19
+ if !PDK.answers['template-url'].nil?
20
+ PDK.answers['template-url']
21
+ elsif PDK::Util.package_install?
22
+ 'file://' + File.join(PDK::Util.package_cachedir, 'pdk-module-template.git')
23
+ else
24
+ 'https://github.com/puppetlabs/pdk-module-template'
25
+ end
26
+ end
19
27
 
20
28
  def self.invoke(opts = {})
21
29
  target_dir = File.expand_path(opts[:target_dir])
@@ -42,9 +50,9 @@ module PDK
42
50
 
43
51
  prepare_module_directory(temp_target_dir)
44
52
 
45
- template_url = opts.fetch(:'template-url', PDK.answers['template-url'] || DEFAULT_TEMPLATE)
53
+ template_url = opts.fetch(:'template-url', default_template_url)
46
54
 
47
- PDK::Module::TemplateDir.new(template_url) do |templates|
55
+ PDK::Module::TemplateDir.new(template_url, metadata.data) do |templates|
48
56
  templates.render do |file_path, file_content|
49
57
  file = Pathname.new(temp_target_dir) + file_path
50
58
  file.dirname.mkpath
@@ -79,7 +87,7 @@ module PDK
79
87
  login_clean = 'username' if login_clean.empty?
80
88
 
81
89
  if login_clean != login
82
- PDK.logger.warn _('You username is not a valid Forge username, proceeding with the username %{username}') % {
90
+ PDK.logger.warn _('Your username is not a valid Forge username, proceeding with the username %{username}. You can fix this afterwards in metadata.json.') % {
83
91
  username: login_clean,
84
92
  }
85
93
  end
@@ -96,6 +104,9 @@ module PDK
96
104
  'dependencies' => [
97
105
  { 'name' => 'puppetlabs-stdlib', 'version_requirement' => '>= 4.13.1 < 5.0.0' },
98
106
  ],
107
+ 'requirements' => [
108
+ { 'name' => 'puppet', 'version_requirement' => '>= 4.7.0 < 6.0.0' },
109
+ ],
99
110
  }
100
111
  defaults['author'] = PDK.answers['author'] unless PDK.answers['author'].nil?
101
112
  defaults['license'] = PDK.answers['license'] unless PDK.answers['license'].nil?
@@ -130,8 +141,8 @@ module PDK
130
141
  questions = [
131
142
  {
132
143
  name: 'name',
133
- question: _('What is your Puppet Forge username?'),
134
- help: _('This will be used when uploading your module to the Forge. You can opt out of this at any time.'),
144
+ question: _('If you have a Puppet Forge username, add it here.'),
145
+ help: _('We can use this to upload your module to the Forge when it\'s complete.'),
135
146
  required: true,
136
147
  validate_pattern: %r{\A[a-z0-9]+\Z}i,
137
148
  validate_message: _('Forge usernames can only contain lowercase letters and numbers'),
@@ -149,7 +160,7 @@ module PDK
149
160
  {
150
161
  name: 'author',
151
162
  question: _('Who wrote this module?'),
152
- help: _('The person who gets credit for creating the module. '),
163
+ help: _('This will be used to credit the module\'s author.'),
153
164
  required: true,
154
165
  default: metadata.data['author'],
155
166
  },
@@ -162,28 +173,28 @@ module PDK
162
173
  },
163
174
  {
164
175
  name: 'summary',
165
- question: _('How would you describe this module in a single sentence?'),
166
- help: _('To help other Puppet users understand what the module does.'),
176
+ question: _('Please summarize the purpose of this module in a single sentence.'),
177
+ help: _('This will help other Puppet users understand what the module does.'),
167
178
  required: true,
168
179
  default: metadata.data['summary'],
169
180
  },
170
181
  {
171
182
  name: 'source',
172
- question: _("Where is this modules's source code repository?"),
173
- help: _('Usually a GitHub URL'),
183
+ question: _('If there is a source code repository for this module, enter the URL here.'),
184
+ help: _('Skip this if none exists yet, you can update this later in the metadata.json.'),
174
185
  required: true,
175
186
  default: metadata.data['source'],
176
187
  },
177
188
  {
178
189
  name: 'project_page',
179
- question: _('Where can others go to learn more about this module?'),
180
- help: _('A web site that offers full information about your module.'),
190
+ question: _('If there is a URL where others can learn more about this module, enter it here.'),
191
+ help: _('Optional. As with all questions above, you can update this later in the metadata.json.'),
181
192
  default: metadata.data['project_page'],
182
193
  },
183
194
  {
184
195
  name: 'issues_url',
185
- question: _('Where can others go to file issues about this module?'),
186
- help: _('A web site with a public bug tracker for your module.'),
196
+ question: _('If there is a public issue tracker for this module, enter its URL here.'),
197
+ help: _('Optional. As with all questions above, you can update this later in the metadata.json.'),
187
198
  default: metadata.data['issues_url'],
188
199
  },
189
200
  ]
@@ -197,8 +208,11 @@ module PDK
197
208
  interview.add_questions(questions)
198
209
 
199
210
  puts _(
200
- "\nWe need to create a metadata.json file for this module, so we're going to ask you %{count} quick questions.\n" \
201
- "If the question is not applicable to this module, just leave the answer blank.\n\n",
211
+ "\nWe need to create a metadata.json file for this module, so we\'re going to ask you %{count} quick " \
212
+ "questions.\n" \
213
+ 'If the question is not applicable to this module, simply leave the answer blank and skip. A default option ' \
214
+ 'is shown after each question. You can modify this or any other answers at any time by manually updating ' \
215
+ "the metadata.json file.\n\n",
202
216
  ) % { count: interview.num_questions }
203
217
 
204
218
  answers = interview.run
@@ -220,7 +234,11 @@ module PDK
220
234
  puts '-' * 40
221
235
  puts
222
236
 
223
- unless prompt.yes?(_('About to generate this module; continue?'))
237
+ continue = prompt.yes?(_('About to generate this module; continue?')) do |q|
238
+ q.validate(proc { |value| [true, false].include?(value) || value =~ %r{\A(?:yes|y|no|n)\Z}i }, 'Please answer "yes" or "no"')
239
+ end
240
+
241
+ unless continue
224
242
  PDK.logger.info _('Module not generated.')
225
243
  exit 0
226
244
  end
@@ -11,10 +11,7 @@ module PDK
11
11
  # provided to the class and class spec templates during rendering.
12
12
  def template_data
13
13
  data = { name: object_name }
14
- if @options.key?(:params)
15
- data[:params] = @options[:params]
16
- data[:max_type_length] = @options[:params].map { |r| r[:type].length }.max
17
- end
14
+
18
15
  data
19
16
  end
20
17
 
@@ -197,7 +197,7 @@ module PDK
197
197
  @templates ||= [
198
198
  { type: 'CLI', url: @options[:'template-url'], allow_fallback: false },
199
199
  { type: 'metadata', url: module_metadata.data['template-url'], allow_fallback: true },
200
- { type: 'default', url: PDK::Generate::Module::DEFAULT_TEMPLATE, allow_fallback: false },
200
+ { type: 'default', url: PDK::Generate::Module.default_template_url, allow_fallback: false },
201
201
  ]
202
202
  end
203
203
 
@@ -20,5 +20,9 @@ module PDK
20
20
  def enable_debug_output
21
21
  self.level = ::Logger::DEBUG
22
22
  end
23
+
24
+ def debug?
25
+ level == ::Logger::DEBUG
26
+ end
23
27
  end
24
28
  end
@@ -34,6 +34,7 @@ module PDK
34
34
  'operatingsystemrelease' => ['2012 R2'],
35
35
  },
36
36
  ],
37
+ 'requirements' => Set.new.freeze,
37
38
  }.freeze
38
39
 
39
40
  def initialize(params = {})
@@ -15,6 +15,8 @@ module PDK
15
15
  #
16
16
  # @param path_or_url [String] The path to a directory to use as the
17
17
  # template or a URL to a git repository.
18
+ # @param module_metadata [Hash] A Hash containing the module metadata.
19
+ # Defaults to an empty Hash.
18
20
  # @yieldparam self [PDK::Module::TemplateDir] The initialised object with
19
21
  # the template available on disk.
20
22
  #
@@ -34,7 +36,7 @@ module PDK
34
36
  # @raise [ArgumentError] (see #validate_module_template!)
35
37
  #
36
38
  # @api public
37
- def initialize(path_or_url)
39
+ def initialize(path_or_url, module_metadata = {})
38
40
  if File.directory?(path_or_url)
39
41
  @path = path_or_url
40
42
  else
@@ -61,6 +63,8 @@ module PDK
61
63
  @object_dir = File.join(@path, 'object_templates')
62
64
  validate_module_template!
63
65
 
66
+ @module_metadata = module_metadata
67
+
64
68
  yield self
65
69
  ensure
66
70
  # If we cloned a git repo to get the template, remove the clone once
@@ -81,7 +85,7 @@ module PDK
81
85
  def metadata
82
86
  return {} unless @repo
83
87
 
84
- ref_result = PDK::CLI::Exec.git('--git-dir', File.join(@path, '.git'), 'describe', '--all', '--long')
88
+ ref_result = PDK::CLI::Exec.git('--git-dir', File.join(@path, '.git'), 'describe', '--all', '--long', '--always')
85
89
  if ref_result[:exit_code].zero?
86
90
  { 'template-url' => @repo, 'template-ref' => ref_result[:stdout].strip }
87
91
  else
@@ -231,7 +235,9 @@ module PDK
231
235
  end
232
236
 
233
237
  file_config = @config.fetch(:global, {})
234
- file_config.merge(@config.fetch(dest_path, {})) unless dest_path.nil?
238
+ file_config['module_metadata'] = @module_metadata
239
+ file_config.merge!(@config.fetch(dest_path, {})) unless dest_path.nil?
240
+ file_config
235
241
  end
236
242
  end
237
243
  end
@@ -0,0 +1,13 @@
1
+ require 'tty-prompt'
2
+
3
+ module TTY
4
+ class Prompt
5
+ class Reader
6
+ class WinConsole
7
+ def get_char_non_blocking # rubocop:disable Style/AccessorMethodName
8
+ WinAPI.getch.chr
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -96,7 +96,7 @@ module PDK
96
96
  location = nil if location.empty?
97
97
 
98
98
  # TODO: maybe add trace
99
- [location, severity, message].compact.join(': ')
99
+ [severity, source, location, message].compact.join(': ')
100
100
  end
101
101
 
102
102
  # Renders the event as a JUnit XML testcase.
@@ -170,7 +170,9 @@ module PDK
170
170
 
171
171
  if path.absolute?
172
172
  module_root = Pathname.new(PDK::Util.module_root)
173
- path.relative_path_from(module_root).to_path
173
+ path = path.relative_path_from(module_root).to_path
174
+ path << '/' if path == '.'
175
+ path
174
176
  else
175
177
  path.to_path
176
178
  end
@@ -152,5 +152,19 @@ module PDK
152
152
  json_result
153
153
  end
154
154
  module_function :find_valid_json_in
155
+
156
+ # Returns the targets' paths relative to the working directory
157
+ #
158
+ # @return [Array<String>] The absolute or path to the target
159
+ def targets_relative_to_pwd(targets)
160
+ targets.map do |t|
161
+ if Pathname.new(t).absolute?
162
+ Pathname.new(t).relative_path_from(Pathname.pwd)
163
+ else
164
+ t
165
+ end
166
+ end
167
+ end
168
+ module_function :targets_relative_to_pwd
155
169
  end
156
170
  end
@@ -1,6 +1,5 @@
1
1
  require 'bundler'
2
2
  require 'fileutils'
3
- require 'tty-spinner'
4
3
  require 'pdk/util'
5
4
  require 'pdk/cli/exec'
6
5
 
@@ -83,8 +82,7 @@ module PDK
83
82
  result = bundle_command(*argv).execute!
84
83
 
85
84
  unless result[:exit_code].zero?
86
- $stderr.puts result[:stdout]
87
- $stderr.puts result[:stderr]
85
+ PDK.logger.debug(result.values_at(:stdout, :stderr).join("\n"))
88
86
  end
89
87
 
90
88
  result[:exit_code].zero?
@@ -98,15 +96,14 @@ module PDK
98
96
  result = command.execute!
99
97
 
100
98
  unless result[:exit_code].zero?
101
- $stderr.puts result[:stdout]
102
- $stderr.puts result[:stderr]
99
+ PDK.logger.fatal(result.values_at(:stdout, :stderr).join("\n"))
103
100
  end
104
101
 
105
102
  result[:exit_code].zero?
106
103
  end
107
104
 
108
105
  def install!
109
- argv = ['install', "--gemfile=#{gemfile}"]
106
+ argv = ['install', "--gemfile=#{gemfile}", '-j4']
110
107
  argv << "--path=#{bundle_cachedir}" if PDK::Util.gem_install?
111
108
 
112
109
  command = bundle_command(*argv).tap do |c|
@@ -116,8 +113,7 @@ module PDK
116
113
  result = command.execute!
117
114
 
118
115
  unless result[:exit_code].zero?
119
- $stderr.puts result[:stdout]
120
- $stderr.puts result[:stderr]
116
+ PDK.logger.fatal(result.values_at(:stdout, :stderr).join("\n"))
121
117
  end
122
118
 
123
119
  result[:exit_code].zero?
@@ -132,9 +128,7 @@ module PDK
132
128
  result = command.execute!
133
129
 
134
130
  unless result[:exit_code].zero?
135
- PDK.logger.error(_('Failed to generate binstubs for %{gems}') % { gems: gems.join(' ') })
136
- $stderr.puts result[:stdout]
137
- $stderr.puts result[:stderr]
131
+ PDK.logger.fatal(_("Failed to generate binstubs for '%{gems}':\n%{output}") % { gems: gems.join(' '), output: result.values_at(:stdout, :stderr).join("\n") })
138
132
  end
139
133
 
140
134
  result[:exit_code].zero?