pdk 0.6.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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?