puppetlabs_spec_helper 2.6.2 → 4.0.1

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.
@@ -1,10 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'fileutils'
2
4
  require 'rake'
3
5
  require 'rspec/core/rake_task'
4
6
  require 'tmpdir'
5
- require 'yaml'
6
7
  require 'pathname'
7
8
  require 'puppetlabs_spec_helper/version'
9
+ require 'puppetlabs_spec_helper/tasks/fixtures'
10
+ require 'puppetlabs_spec_helper/tasks/check_symlinks'
11
+ require 'English'
8
12
 
9
13
  # optional gems
10
14
  begin
@@ -13,6 +17,24 @@ rescue LoadError
13
17
  # ignore
14
18
  end
15
19
 
20
+ begin
21
+ require 'puppet_blacksmith/rake_tasks'
22
+ rescue LoadError
23
+ # ignore
24
+ end
25
+
26
+ begin
27
+ require 'github_changelog_generator/task'
28
+ rescue LoadError
29
+ # ignore
30
+ end
31
+
32
+ begin
33
+ require 'puppet-strings/tasks'
34
+ rescue LoadError
35
+ # ignore
36
+ end
37
+
16
38
  parallel_tests_loaded = false
17
39
  begin
18
40
  require 'parallel_tests'
@@ -21,535 +43,177 @@ rescue LoadError
21
43
  # ignore
22
44
  end
23
45
 
46
+ task default: [:help]
24
47
 
25
- task :default => [:help]
26
-
27
- pattern = 'spec/{aliases,classes,defines,unit,functions,hosts,integration,type_aliases,types}/**/*_spec.rb'
48
+ pattern = 'spec/{aliases,classes,defines,functions,hosts,integration,plans,tasks,type_aliases,types,unit}/**/*_spec.rb'
28
49
 
29
50
  RSpec::Core::RakeTask.new(:spec_standalone) do |t, args|
30
- t.rspec_opts = ['--color']
51
+ t.rspec_opts = []
31
52
  t.rspec_opts << ENV['CI_SPEC_OPTIONS'] unless ENV['CI_SPEC_OPTIONS'].nil?
32
53
  if ENV['CI_NODE_TOTAL'] && ENV['CI_NODE_INDEX']
33
54
  ci_total = ENV['CI_NODE_TOTAL'].to_i
34
55
  ci_index = ENV['CI_NODE_INDEX'].to_i
35
56
  raise "CI_NODE_INDEX must be between 1-#{ci_total}" unless ci_index >= 1 && ci_index <= ci_total
57
+
36
58
  files = Rake::FileList[pattern].to_a
37
59
  per_node = (files.size / ci_total.to_f).ceil
38
60
  t.pattern = if args.extras.nil? || args.extras.empty?
39
61
  files.each_slice(per_node).to_a[ci_index - 1] || files.first
40
62
  else
41
- args.extras.join(",")
63
+ args.extras.join(',')
42
64
  end
43
65
  else
44
- if args.extras.nil? || args.extras.empty?
45
- t.pattern = pattern
46
- else
47
- t.pattern = args.extras.join(",")
48
- end
66
+ t.pattern = if args.extras.nil? || args.extras.empty?
67
+ pattern
68
+ else
69
+ args.extras.join(',')
70
+ end
49
71
  end
50
72
  end
51
73
 
52
- desc "List spec tests in a JSON document"
74
+ desc 'List spec tests in a JSON document'
53
75
  RSpec::Core::RakeTask.new(:spec_list_json) do |t|
54
76
  t.rspec_opts = ['--dry-run', '--format', 'json']
55
77
  t.pattern = pattern
56
78
  end
57
79
 
58
- desc "Run beaker acceptance tests"
59
- RSpec::Core::RakeTask.new(:beaker) do |t|
60
- t.rspec_opts = ['--color']
61
- t.pattern = 'spec/acceptance'
62
- # TEST_TIERS env variable is a comma separated list of tiers to run. e.g. low, medium, high
63
- if ENV['TEST_TIERS']
64
- tiers = '--tag '
65
- test_tiers = ENV['TEST_TIERS'].split(',')
66
- raise 'TEST_TIERS env variable must have at least 1 tier specified. low, medium or high (comma separated).' if test_tiers.count == 0
67
- test_tiers.each do |tier|
68
- tier_to_add = tier.strip
69
- raise "#{tier_to_add} not a valid test tier." unless %w(low medium high).include?(tier_to_add)
70
- tiers += "tier_#{tier_to_add},"
71
- end
72
- tiers = tiers.chomp(',')
73
- t.rspec_opts.push(tiers)
74
- else
75
- puts 'TEST_TIERS env variable not defined. Defaulting to run all tests.'
76
- end
77
- end
78
-
79
- module PuppetlabsSpecHelper::RakeTasks
80
- # This is a helper for the self-symlink entry of fixtures.yml
81
- def source_dir
82
- Dir.pwd
83
- end
84
-
85
- # cache the repositories and return a hash object
86
- def repositories
87
- unless @repositories
88
- @repositories = fixtures('repositories')
89
- end
90
- @repositories
91
- end
92
-
93
- # get the array of Beaker set names
94
- # @return [Array<String>]
95
- def beaker_node_sets
96
- return @beaker_nodes if @beaker_nodes
97
- @beaker_nodes = Dir['spec/acceptance/nodesets/*.yml'].sort.map do |node_set|
98
- node_set.slice!('.yml')
99
- File.basename(node_set)
100
- end
101
- end
102
-
103
- # Use "vagrant ssh" to login to the given node in the node set
104
- # @param set [String] The name of the node set (yml file)
105
- # @param node [String] The name of the node in the set. For multi-node sets.
106
- def vagrant_ssh(set, node = nil)
107
- vagrant_yml_dir = File.join '.vagrant', 'beaker_vagrant_files', "#{set}.yml"
108
- vagrant_file = File.join vagrant_yml_dir, 'Vagrantfile'
109
- unless File.file? vagrant_file
110
- puts "There is no Vagrantfile at: '#{vagrant_file}'. Perhaps, the node is not created or is destroyed."
111
- exit 1
112
- end
113
- Dir.chdir(vagrant_yml_dir) do
114
- command = 'vagrant ssh'
115
- command += " #{node}" if node
116
- # Vagrant is not distributed as a normal gem
117
- # and we should protect it from the current Ruby environment
118
- env = {
119
- 'RUBYLIB' => nil,
120
- 'GEM_PATH' => nil,
121
- 'BUNDLE_BIN_PATH' => nil,
122
- }
123
- system env, command
124
- end
125
- end
126
-
127
- def auto_symlink
128
- { File.basename(Dir.pwd).split('-').last => '#{source_dir}' }
129
- end
130
-
131
- def fixtures(category)
132
- if ENV['FIXTURES_YML']
133
- fixtures_yaml = ENV['FIXTURES_YML']
134
- elsif File.exists?('.fixtures.yml')
135
- fixtures_yaml = '.fixtures.yml'
136
- elsif File.exists?('.fixtures.yaml')
137
- fixtures_yaml = '.fixtures.yaml'
138
- else
139
- fixtures_yaml = false
140
- end
141
-
142
- begin
143
- if fixtures_yaml
144
- fixtures = YAML.load_file(fixtures_yaml) || { 'fixtures' => {} }
145
- else
146
- fixtures = { 'fixtures' => {} }
147
- end
148
- rescue Errno::ENOENT
149
- fail("Fixtures file not found: '#{fixtures_yaml}'")
150
- rescue Psych::SyntaxError => e
151
- fail("Found malformed YAML in '#{fixtures_yaml}' on line #{e.line} column #{e.column}: #{e.problem}")
152
- end
153
-
154
- unless fixtures.include?('fixtures')
155
- # File is non-empty, but does not specify fixtures
156
- fail("No 'fixtures' entries found in '#{fixtures_yaml}'; required")
157
- end
158
-
159
- if fixtures.include? 'defaults'
160
- fixture_defaults = fixtures['defaults']
161
- else
162
- fixture_defaults = {}
163
- end
164
-
165
- fixtures = fixtures['fixtures']
166
-
167
- if fixtures['symlinks'].nil?
168
- fixtures['symlinks'] = auto_symlink
169
- end
170
-
171
- result = {}
172
- if fixtures.include? category and fixtures[category] != nil
173
-
174
- defaults = { "target" => "spec/fixtures/modules" }
175
-
176
- # load defaults from the `.fixtures.yml` `defaults` section
177
- # for the requested category and merge them into my defaults
178
- if fixture_defaults.include? category
179
- defaults = defaults.merge(fixture_defaults[category])
180
- end
181
-
182
- fixtures[category].each do |fixture, opts|
183
- # convert a simple string fixture to a hash, by
184
- # using the string fixture as the `repo` option of the hash.
185
- if opts.instance_of?(String)
186
- opts = { "repo" => opts }
187
- end
188
- # there should be a warning or something if it's not a hash...
189
- if opts.instance_of?(Hash)
190
- # merge our options into the defaults to get the
191
- # final option list
192
- opts = defaults.merge(opts)
193
-
194
- real_target = eval('"'+opts["target"]+'"')
195
- real_source = eval('"'+opts["repo"]+'"')
196
-
197
- result[real_source] = { "target" => File.join(real_target,fixture), "ref" => opts["ref"], "branch" => opts["branch"], "scm" => opts["scm"], "flags" => opts["flags"], "subdir" => opts["subdir"]}
198
- end
199
- end
200
- end
201
- return result
202
- end
203
-
204
- def clone_repo(scm, remote, target, subdir=nil, ref=nil, branch=nil, flags = nil)
205
- args = []
206
- case scm
207
- when 'hg'
208
- args.push('clone')
209
- args.push('-b', branch) if branch
210
- args.push(flags) if flags
211
- args.push(remote, target)
212
- when 'git'
213
- args.push('clone')
214
- args.push('--depth 1') unless ref
215
- args.push('-b', branch) if branch
216
- args.push(flags) if flags
217
- args.push(remote, target)
218
- else
219
- fail "Unfortunately #{scm} is not supported yet"
220
- end
221
- result = system("#{scm} #{args.flatten.join ' '}")
222
- unless File::exists?(target)
223
- fail "Failed to clone #{scm} repository #{remote} into #{target}"
224
- end
225
- result
226
- end
227
-
228
- def revision(scm, target, ref)
229
- args = []
230
- case scm
231
- when 'hg'
232
- args.push('update', '--clean', '-r', ref)
233
- when 'git'
234
- args.push('reset', '--hard', ref)
235
- else
236
- fail "Unfortunately #{scm} is not supported yet"
237
- end
238
- system("cd #{target} && #{scm} #{args.flatten.join ' '}")
239
- end
240
-
241
- def remove_subdirectory(target, subdir)
242
- unless subdir.nil?
243
- Dir.mktmpdir {|tmpdir|
244
- FileUtils.mv(Dir.glob("#{target}/#{subdir}/{.[^\.]*,*}"), tmpdir)
245
- FileUtils.rm_rf("#{target}/#{subdir}")
246
- FileUtils.mv(Dir.glob("#{tmpdir}/{.[^\.]*,*}"), "#{target}")
247
- }
248
- end
249
- end
250
-
251
- # creates a logger so we can log events with certain levels
252
- def logger
253
- unless @logger
254
- require 'logger'
255
- if ENV['ENABLE_LOGGER']
256
- level = Logger::DEBUG
257
- else
258
- level = Logger::INFO
259
- end
260
- @logger = Logger.new(STDERR)
261
- @logger.level = level
262
- end
263
- @logger
264
- end
265
-
266
- def module_working_directory
267
- # The problem with the relative path is that PMT doesn't expand the path properly and so passing in a relative path here
268
- # becomes something like C:\somewhere\backslashes/spec/fixtures/work-dir on Windows, and then PMT barfs itself.
269
- # This has been reported as https://tickets.puppetlabs.com/browse/PUP-4884
270
- File.expand_path(ENV['MODULE_WORKING_DIR'] ? ENV['MODULE_WORKING_DIR'] : 'spec/fixtures/work-dir')
271
- end
272
-
273
- # returns the current thread count that is currently active
274
- # a status of false or nil means the thread completed
275
- # so when anything else we count that as a active thread
276
- def current_thread_count(items)
277
- active_threads = items.find_all do |item, opts|
278
- if opts[:thread]
279
- opts[:thread].status
280
- else
281
- false
282
- end
283
- end
284
- logger.debug "Current thread count #{active_threads.count}"
285
- active_threads.count
286
- end
287
-
288
- # returns the max_thread_count
289
- # because we may want to limit ssh or https connections
290
- def max_thread_limit
291
- unless @max_thread_limit
292
- # the default thread count is 10 but can be
293
- # raised by using environment variable MAX_FIXTURE_THREAD_COUNT
294
- if ENV['MAX_FIXTURE_THREAD_COUNT'].to_i > 0
295
- @max_thread_limit = ENV['MAX_FIXTURE_THREAD_COUNT'].to_i
296
- else
297
- @max_thread_limit = 10 # the default
298
- end
299
- end
300
- @max_thread_limit
301
- end
302
-
303
- def check_directory_for_symlinks(dir='.')
304
- dir = Pathname.new(dir) unless dir.is_a?(Pathname)
305
- results = []
306
-
307
- dir.each_child(true) do |child|
308
- if child.symlink?
309
- results << child
310
- elsif child.directory? && child.basename.to_s != '.git'
311
- results.concat(check_directory_for_symlinks(child))
312
- end
313
- end
314
-
315
- results
316
- end
317
- end
318
- include PuppetlabsSpecHelper::RakeTasks
319
-
320
- desc "Create the fixtures directory"
321
- task :spec_prep do
322
- # Ruby only sets File::ALT_SEPARATOR on Windows and Rubys standard library
323
- # uses this to check for Windows
324
- is_windows = !!File::ALT_SEPARATOR
325
- if is_windows
326
- begin
327
- require 'win32/dir'
328
- rescue LoadError
329
- $stderr.puts "win32-dir gem not installed, falling back to executing mklink directly"
330
- end
331
- end
332
-
333
- # git has a race condition creating that directory, that would lead to aborted clone operations
334
- FileUtils::mkdir_p("spec/fixtures/modules")
335
-
336
- repositories.each do |remote, opts|
337
- scm = 'git'
338
- target = opts["target"]
339
- subdir = opts["subdir"]
340
- ref = opts["ref"]
341
- scm = opts["scm"] if opts["scm"]
342
- branch = opts["branch"] if opts["branch"]
343
- flags = opts["flags"]
344
- # get the current active threads that are alive
345
- count = current_thread_count(repositories)
346
- if count < max_thread_limit
347
- logger.debug "New Thread started for #{remote}"
348
- # start up a new thread and store it in the opts hash
349
- opts[:thread] = Thread.new do
350
- clone_repo(scm, remote, target, subdir, ref, branch, flags)
351
- revision(scm, target, ref) if ref
352
- remove_subdirectory(target, subdir) if subdir
353
- end
354
- else
355
- # the last thread started should be the longest wait
356
- item, item_opts = repositories.find_all {|i,o| o.has_key?(:thread)}.last
357
- logger.debug "Waiting on #{item}"
358
- item_opts[:thread].join # wait for the thread to finish
359
- # now that we waited lets try again
360
- redo
361
- end
362
- end
363
-
364
- # wait for all the threads to finish
365
- repositories.each {|remote, opts| opts[:thread].join }
366
-
367
- fixtures("symlinks").each do |target, link|
368
- link = link['target']
369
- unless File.symlink?(link)
370
- logger.info("Creating symlink from #{link} to #{target}")
371
- if is_windows
372
- target = File.join(File.dirname(link), target) unless Pathname.new(target).absolute?
373
- if Dir.respond_to?(:create_junction)
374
- Dir.create_junction(link, target)
375
- else
376
- system("call mklink /J \"#{link.gsub('/', '\\')}\" \"#{target.gsub('/', '\\')}\"")
377
- end
378
- else
379
- FileUtils::ln_sf(target, link)
380
- end
381
- end
382
- end
383
-
384
- fixtures("forge_modules").each do |remote, opts|
385
- ref = ""
386
- flags = ""
387
- if opts.instance_of?(String)
388
- target = opts
389
- elsif opts.instance_of?(Hash)
390
- target = opts["target"]
391
- ref = " --version #{opts['ref']}" if not opts['ref'].nil?
392
- flags = " #{opts['flags']}" if opts['flags']
393
- end
394
- next if File::exists?(target)
395
-
396
- working_dir = module_working_directory
397
- target_dir = File.expand_path('spec/fixtures/modules')
398
-
399
- command = "puppet module install" + ref + flags + \
400
- " --ignore-dependencies" \
401
- " --force" \
402
- " --module_working_dir \"#{working_dir}\"" \
403
- " --target-dir \"#{target_dir}\" \"#{remote}\""
404
-
405
- unless system(command)
406
- fail "Failed to install module #{remote} to #{target_dir}"
407
- end
80
+ desc 'Run spec tests and clean the fixtures directory if successful'
81
+ task :spec do |_t, args|
82
+ begin
83
+ Rake::Task[:spec_prep].invoke
84
+ Rake::Task[:spec_standalone].invoke(*args.extras)
85
+ Rake::Task[:spec_clean].invoke
86
+ ensure
87
+ Rake::Task[:spec_clean_symlinks].invoke
408
88
  end
409
-
410
- FileUtils::mkdir_p("spec/fixtures/manifests")
411
- FileUtils::touch("spec/fixtures/manifests/site.pp")
412
89
  end
413
90
 
414
- desc "Clean up the fixtures directory"
415
- task :spec_clean do
416
- fixtures("repositories").each do |remote, opts|
417
- target = opts["target"]
418
- FileUtils::rm_rf(target)
91
+ desc 'Run spec tests with ruby simplecov code coverage'
92
+ namespace :spec do
93
+ task :simplecov do
94
+ ENV['SIMPLECOV'] = 'yes'
95
+ Rake::Task['spec'].execute
419
96
  end
420
-
421
- fixtures("forge_modules").each do |remote, opts|
422
- target = opts["target"]
423
- FileUtils::rm_rf(target)
424
- end
425
-
426
- FileUtils::rm_rf(module_working_directory)
427
-
428
- fixtures("symlinks").each do |source, opts|
429
- target = opts["target"]
430
- FileUtils::rm_f(target)
431
- end
432
-
433
- if File.zero?("spec/fixtures/manifests/site.pp")
434
- FileUtils::rm_f("spec/fixtures/manifests/site.pp")
435
- end
436
-
437
97
  end
438
98
 
439
- desc "Run spec tests and clean the fixtures directory if successful"
440
- task :spec do |t, args|
99
+ desc 'Run spec tests in parallel and clean the fixtures directory if successful'
100
+ task :parallel_spec do |_t, args|
441
101
  begin
442
102
  Rake::Task[:spec_prep].invoke
443
- Rake::Task[:spec_standalone].invoke(*args.extras)
444
- ensure
103
+ Rake::Task[:parallel_spec_standalone].invoke(*args.extras)
445
104
  Rake::Task[:spec_clean].invoke
105
+ ensure
106
+ Rake::Task[:spec_clean_symlinks].invoke
446
107
  end
447
108
  end
448
109
 
449
- desc "Parallel spec tests"
450
- task :parallel_spec do
110
+ desc 'Parallel spec tests'
111
+ task :parallel_spec_standalone do |_t, args|
451
112
  raise 'Add the parallel_tests gem to Gemfile to enable this task' unless parallel_tests_loaded
113
+
452
114
  if Rake::FileList[pattern].to_a.empty?
453
- warn "No files for parallel_spec to run against"
115
+ warn 'No files for parallel_spec to run against'
454
116
  else
455
- begin
456
- args = ['-t', 'rspec']
457
- args.push('--').concat(ENV['CI_SPEC_OPTIONS'].strip.split(' ')).push('--') unless ENV['CI_SPEC_OPTIONS'].nil? || ENV['CI_SPEC_OPTIONS'].strip.empty?
458
- args.concat(Rake::FileList[pattern].to_a)
459
-
460
- Rake::Task[:spec_prep].invoke
461
- ParallelTests::CLI.new.run(args)
462
- ensure
463
- Rake::Task[:spec_clean].invoke
464
- end
465
- end
466
- end
467
117
 
468
- desc "List available beaker nodesets"
469
- task 'beaker:sets' do
470
- beaker_node_sets.each do |set|
471
- puts set
472
- end
473
- end
474
-
475
- # alias for compatibility
476
- task 'beaker_nodes' => 'beaker:sets'
118
+ args = ['-t', 'rspec']
119
+ args.push('--').concat(ENV['CI_SPEC_OPTIONS'].strip.split(' ')).push('--') unless ENV['CI_SPEC_OPTIONS'].nil? || ENV['CI_SPEC_OPTIONS'].strip.empty?
120
+ args.concat(Rake::FileList[pattern].to_a)
477
121
 
478
- desc 'Try to use vagrant to login to the Beaker node'
479
- task 'beaker:ssh', [:set, :node] do |_task, args|
480
- set = args[:set] || ENV['BEAKER_set'] || ENV['RS_SET'] || 'default'
481
- node = args[:node]
482
- vagrant_ssh set, node
483
- end
122
+ ParallelTests::CLI.new.run(args)
484
123
 
485
- beaker_node_sets.each do |set|
486
- desc "Run the Beaker acceptance tests for the node set '#{set}'"
487
- task "beaker:#{set}" do
488
- ENV['BEAKER_set'] = set
489
- Rake::Task['beaker'].reenable
490
- Rake::Task['beaker'].invoke
491
124
  end
125
+ end
492
126
 
493
- desc "Use vagrant to login to a node from the set '#{set}'"
494
- task "beaker:ssh:#{set}", [:node] do |_task, args|
495
- node = args[:node]
496
- vagrant_ssh set, node
127
+ desc 'Build puppet module package'
128
+ task :build do
129
+ if Gem::Specification.find_by_name('puppet').version < Gem::Version.new('6.0.0')
130
+ Rake::Task['build:pmt'].invoke
131
+ else
132
+ Rake::Task['build:pdk'].invoke
497
133
  end
498
134
  end
499
135
 
500
- desc "Build puppet module package"
501
- task :build do
502
- # This will be deprecated once puppet-module is a face.
503
- begin
504
- Gem::Specification.find_by_name('puppet-module')
505
- rescue Gem::LoadError, NoMethodError
136
+ namespace :build do
137
+ desc 'Build Puppet module package with PMT (Puppet < 6.0.0 only)'
138
+ task :pmt do
506
139
  require 'puppet/face'
140
+
507
141
  pmod = Puppet::Face['module', :current]
508
142
  pmod.build('./')
509
143
  end
144
+
145
+ desc 'Build Puppet module with PDK'
146
+ task :pdk do
147
+ begin
148
+ require 'pdk/util'
149
+ require 'pdk/module/build'
150
+
151
+ path = PDK::Module::Build.invoke(force: true, 'target-dir': File.join(Dir.pwd, 'pkg'))
152
+ puts "Module built: #{path}"
153
+ rescue LoadError
154
+ _ = `pdk --version`
155
+ unless $CHILD_STATUS.success?
156
+ warn 'Unable to build module. Please install PDK or add the `pdk` gem to your Gemfile.'
157
+ abort
158
+ end
159
+
160
+ system('pdk build --force')
161
+ end
162
+ end
510
163
  end
511
164
 
512
- desc "Clean a built module package"
165
+ desc 'Clean a built module package'
513
166
  task :clean do
514
- FileUtils.rm_rf("pkg/")
167
+ FileUtils.rm_rf('pkg/')
515
168
  end
516
169
 
517
170
  require 'puppet-lint/tasks/puppet-lint'
518
171
  # Must clear as it will not override the existing puppet-lint rake task since we require to import for
519
172
  # the PuppetLint::RakeTask
520
173
  Rake::Task[:lint].clear
521
- # Relative is not able to be set within the context of PuppetLint::RakeTask
174
+ # Utilize PuppetLint global configuration so that these settings can be tweaked by
175
+ # spec_helper.rb in an individual module
522
176
  PuppetLint.configuration.relative = true
523
- PuppetLint::RakeTask.new(:lint) do |config|
524
- config.fail_on_warnings = true
525
- config.disable_checks = [
526
- '80chars',
527
- '140chars',
528
- 'class_inherits_from_params_class',
529
- 'class_parameter_defaults',
530
- 'documentation',
531
- 'single_quote_string_with_variables']
532
- config.ignore_paths = [
533
- "bundle/**/*.pp",
534
- "pkg/**/*.pp",
535
- "spec/**/*.pp",
536
- "tests/**/*.pp",
537
- "types/**/*.pp",
538
- "vendor/**/*.pp",
539
- ]
177
+ PuppetLint.configuration.ignore_paths ||= []
178
+ PuppetLint.configuration.ignore_paths << '.vendor/**/*.pp'
179
+ PuppetLint.configuration.ignore_paths << 'bundle/**/*.pp'
180
+ PuppetLint.configuration.ignore_paths << 'pkg/**/*.pp'
181
+ PuppetLint.configuration.ignore_paths << 'spec/**/*.pp'
182
+ PuppetLint.configuration.ignore_paths << 'tests/**/*.pp'
183
+ PuppetLint.configuration.ignore_paths << 'types/**/*.pp'
184
+ PuppetLint.configuration.ignore_paths << 'vendor/**/*.pp'
185
+ puppet_lint_disable_checks = %w[
186
+ 80chars
187
+ 140chars
188
+ class_inherits_from_params_class
189
+ class_parameter_defaults
190
+ disable_autoloader_layout
191
+ documentation
192
+ single_quote_string_with_variables
193
+ ]
194
+ puppet_lint_disable_checks.each do |check|
195
+ PuppetLint.configuration.send("disable_#{check}")
196
+ end
197
+ PuppetLint::RakeTask.new(:lint)
198
+
199
+ desc 'Run puppet-lint and fix issues automatically'
200
+ PuppetLint::RakeTask.new(:lint_fix) do |config|
201
+ config.fix = true
540
202
  end
541
203
 
542
204
  require 'puppet-syntax/tasks/puppet-syntax'
543
205
  PuppetSyntax.exclude_paths ||= []
544
- PuppetSyntax.exclude_paths << "spec/fixtures/**/*"
545
- PuppetSyntax.exclude_paths << "pkg/**/*"
546
- PuppetSyntax.exclude_paths << "vendor/**/*"
206
+ PuppetSyntax.exclude_paths << 'spec/fixtures/**/*'
207
+ PuppetSyntax.exclude_paths << 'pkg/**/*'
208
+ PuppetSyntax.exclude_paths << 'vendor/**/*'
209
+ PuppetSyntax.exclude_paths << '.vendor/**/*'
210
+ PuppetSyntax.exclude_paths << 'plans/**/*'
547
211
  if Puppet.version.to_f < 4.0
548
- PuppetSyntax.exclude_paths << "types/**/*"
212
+ PuppetSyntax.exclude_paths << 'types/**/*'
549
213
  end
550
214
  PuppetSyntax.future_parser = true if ENV['FUTURE_PARSER'] == 'yes'
551
215
 
552
- desc "Check syntax of Ruby files and call :syntax and :metadata_lint"
216
+ desc 'Check syntax of Ruby files and call :syntax and :metadata_lint'
553
217
  task :validate do
554
218
  Dir['lib/**/*.rb'].each do |lib_file|
555
219
  sh "ruby -c #{lib_file}"
@@ -560,7 +224,7 @@ task :validate do
560
224
  if Rake::Task.task_defined?(:metadata_lint)
561
225
  Rake::Task[:metadata_lint].invoke
562
226
  else
563
- warn "Skipping metadata validation; the metadata-json-lint gem was not found"
227
+ warn 'Skipping metadata validation; the metadata-json-lint gem was not found'
564
228
  end
565
229
  end
566
230
  end
@@ -570,23 +234,23 @@ task :metadata do
570
234
  if Rake::Task.task_defined?(:metadata_lint)
571
235
  Rake::Task[:metadata_lint].invoke
572
236
  else
573
- warn "Skipping metadata validation; the metadata-json-lint gem was not found"
237
+ warn 'Skipping metadata validation; the metadata-json-lint gem was not found'
574
238
  end
575
239
  end
576
240
 
577
- desc "Print development version of module"
241
+ desc 'Print development version of module'
578
242
  task :compute_dev_version do
579
243
  version = ''
580
- if File.exists?( 'metadata.json' )
244
+ if File.exist?('metadata.json')
581
245
  require 'json'
582
246
 
583
- modinfo = JSON.parse(File.read( 'metadata.json' ))
247
+ modinfo = JSON.parse(File.read('metadata.json'))
584
248
  version = modinfo['version']
585
- elsif File.exists?( 'Modulefile' )
249
+ elsif File.exist?('Modulefile')
586
250
  modfile = File.read('Modulefile')
587
- version = modfile.match(/\nversion[ ]+['"](.*)['"]/)[1]
251
+ version = modfile.match(%r{\nversion +['"](.*)['"]})[1]
588
252
  else
589
- fail "Could not find a metadata.json or Modulefile! Cannot compute dev version without one or the other!"
253
+ raise 'Could not find a metadata.json or Modulefile! Cannot compute dev version without one or the other!'
590
254
  end
591
255
 
592
256
  sha = `git rev-parse HEAD`[0..7]
@@ -594,22 +258,22 @@ task :compute_dev_version do
594
258
 
595
259
  # If we're in a CI environment include our build number
596
260
  # If the branch is a release branch we append an 'r' into the new_version,
597
- # this is due to the release branch buildID conflicting with master branch when trying to push to the staging forge.
261
+ # this is due to the release branch buildID conflicting with main branch when trying to push to the staging forge.
598
262
  # More info can be found at https://tickets.puppetlabs.com/browse/FM-6170
599
- if build = ENV['BUILD_NUMBER'] || ENV['TRAVIS_BUILD_NUMBER']
600
- if branch.eql? "release"
601
- new_version = sprintf('%s-%s%04d-%s', version, "r", build, sha)
602
- else
603
- new_version = sprintf('%s-%04d-%s', version, build, sha)
604
- end
605
- else
606
- new_version = "#{version}-#{sha}"
607
- end
263
+ new_version = if build = (ENV['BUILD_NUMBER'] || ENV['TRAVIS_BUILD_NUMBER'])
264
+ if branch.eql? 'release'
265
+ '%s-%s%04d-%s' % [version, 'r', build, sha] # legacy support code # rubocop:disable Style/FormatStringToken
266
+ else
267
+ '%s-%04d-%s' % [version, build, sha] # legacy support code # rubocop:disable Style/FormatStringToken
268
+ end
269
+ else
270
+ "#{version}-#{sha}"
271
+ end
608
272
 
609
273
  print new_version
610
274
  end
611
275
 
612
- desc "Runs all necessary checks on a module in preparation for a release"
276
+ desc 'Runs all necessary checks on a module in preparation for a release'
613
277
  task :release_checks do
614
278
  Rake::Task[:lint].invoke
615
279
  Rake::Task[:validate].invoke
@@ -618,56 +282,53 @@ task :release_checks do
618
282
  else
619
283
  Rake::Task[:spec].invoke
620
284
  end
621
- Rake::Task["check:symlinks"].invoke
622
- Rake::Task["check:test_file"].invoke
623
- Rake::Task["check:dot_underscore"].invoke
624
- Rake::Task["check:git_ignore"].invoke
285
+ Rake::Task[:check].invoke
625
286
  end
626
287
 
627
288
  namespace :check do
628
- desc "Fails if symlinks are present in directory"
289
+ desc 'Fails if symlinks are present in directory'
629
290
  task :symlinks do
630
- symlinks = check_directory_for_symlinks
291
+ symlinks = PuppetlabsSpecHelper::Tasks::CheckSymlinks.new.check
631
292
  unless symlinks.empty?
632
- symlinks.each { |r| puts "Symlink found: #{r.to_s} => #{r.readlink}" }
633
- fail "Symlink(s) exist within this directory"
293
+ symlinks.each { |r| puts "Symlink found: #{r} => #{r.readlink}" }
294
+ raise 'Symlink(s) exist within this directory'
634
295
  end
635
296
  end
636
297
 
637
- desc "Fails if .pp files present in tests folder"
298
+ desc 'Fails if .pp files present in tests folder'
638
299
  task :test_file do
639
- if Dir.exist?("tests")
640
- Dir.chdir("tests")
641
- ppfiles = Dir["*.pp"]
642
- unless ppfiles.empty?
643
- puts ppfiles
644
- fail ".pp files present in tests folder; Move them to an examples folder following the new convention"
645
- end
300
+ ppfiles = Dir[File.join('tests', '**', '*.pp')]
301
+ unless ppfiles.empty?
302
+ puts ppfiles
303
+ raise '.pp files present in tests folder; Move them to an examples folder following the new convention'
646
304
  end
647
305
  end
648
306
 
649
- desc "Fails if any ._ files are present in directory"
307
+ desc 'Fails if any ._ files are present in directory'
650
308
  task :dot_underscore do
651
- dirs = Dir["._*"]
309
+ dirs = Dir['._*']
652
310
  unless dirs.empty?
653
311
  puts dirs
654
- fail "._ files are present in the directory"
312
+ raise '._ files are present in the directory'
655
313
  end
656
314
  end
657
315
 
658
- desc "Fails if directories contain the files specified in .gitignore"
316
+ desc 'Fails if directories contain the files specified in .gitignore'
659
317
  task :git_ignore do
660
318
  matched = `git ls-files --ignored --exclude-standard`
661
- unless matched == ""
319
+ unless matched == ''
662
320
  puts matched
663
- fail "File specified in .gitignore has been committed"
321
+ raise 'File specified in .gitignore has been committed'
664
322
  end
665
323
  end
666
324
  end
667
325
 
668
- desc "Display the list of available rake tasks"
326
+ desc 'Run static pre release checks'
327
+ task check: ['check:symlinks', 'check:test_file', 'check:dot_underscore', 'check:git_ignore']
328
+
329
+ desc 'Display the list of available rake tasks'
669
330
  task :help do
670
- system("rake -T")
331
+ system('rake -T')
671
332
  end
672
333
 
673
334
  begin
@@ -675,33 +336,99 @@ begin
675
336
  RuboCop::RakeTask.new(:rubocop) do |task|
676
337
  # These make the rubocop experience maybe slightly less terrible
677
338
  task.options = ['-D', '-S', '-E']
339
+
340
+ # Use Rubocop's Github Actions formatter if possible
341
+ if ENV['GITHUB_ACTIONS'] == 'true'
342
+ rubocop_spec = Gem::Specification.find_by_name('rubocop')
343
+ if Gem::Version.new(rubocop_spec.version) >= Gem::Version.new('1.2')
344
+ task.formatters << 'github'
345
+ end
346
+ end
678
347
  end
679
348
  rescue LoadError
680
- desc "rubocop is not available in this installation"
349
+ desc 'rubocop is not available in this installation'
681
350
  task :rubocop do
682
- fail "rubocop is not available in this installation"
351
+ raise 'rubocop is not available in this installation'
683
352
  end
684
353
  end
685
354
 
686
- module_dir = Dir.pwd
687
- locales_dir = File.absolute_path('locales', module_dir )
688
- # if the task is allowed to run when the module does not have a locales directory,
689
- # the task is run in the puppet gem instead and creates a POT there.
690
- puts "gettext-setup tasks will only be loaded if the locales/ directory is present" if Rake.verbose == true
691
- if File.exist? locales_dir
692
- begin
693
- spec = Gem::Specification.find_by_name 'gettext-setup'
694
- load "#{spec.gem_dir}/lib/tasks/gettext.rake"
695
- # Initialization requires a valid locales directory
696
- GettextSetup.initialize_config(locales_dir)
697
- namespace :module do
698
- desc "Runs all tasks to build a modules POT file for internationalization"
699
- task :pot_gen do
700
- Rake::Task["gettext:pot"].invoke()
701
- Rake::Task["gettext:metadata_pot"].invoke("#{module_dir}/metadata.json")
355
+ def create_gch_task(changelog_user = nil, changelog_project = nil, changelog_since_tag = nil, changelog_tag_pattern = 'v%s')
356
+ if Bundler.rubygems.find_name('github_changelog_generator').any?
357
+ # needed a place to hide these methods
358
+ # rubocop:disable Lint/NestedMethodDefinition
359
+ def changelog_user_from_metadata
360
+ result = JSON.parse(File.read('metadata.json'))['author']
361
+ raise 'unable to find the changelog_user in .sync.yml, or the author in metadata.json' if result.nil?
362
+
363
+ puts "GitHubChangelogGenerator user:#{result}"
364
+ result
365
+ end
366
+
367
+ def changelog_project_from_metadata
368
+ result = JSON.parse(File.read('metadata.json'))['name']
369
+ raise 'unable to find the changelog_project in .sync.yml or the name in metadata.json' if result.nil?
370
+
371
+ puts "GitHubChangelogGenerator project:#{result}"
372
+ result
373
+ end
374
+
375
+ def changelog_future_release
376
+ return unless Rake.application.top_level_tasks.include? 'changelog'
377
+
378
+ result = JSON.parse(File.read('metadata.json'))['version']
379
+ raise 'unable to find the future_release (version) in metadata.json' if result.nil?
380
+
381
+ puts "GitHubChangelogGenerator future_release:#{result}"
382
+ result
383
+ end
384
+ # rubocop:enable Lint/NestedMethodDefinition
385
+
386
+ GitHubChangelogGenerator::RakeTask.new :changelog do |config|
387
+ if ENV['CHANGELOG_GITHUB_TOKEN'].nil?
388
+ raise "Set CHANGELOG_GITHUB_TOKEN environment variable eg 'export CHANGELOG_GITHUB_TOKEN=valid_token_here'"
702
389
  end
390
+
391
+ config.user = changelog_user || changelog_user_from_metadata
392
+ config.project = changelog_project || changelog_project_from_metadata
393
+ config.since_tag = changelog_since_tag if changelog_since_tag
394
+ config.future_release = changelog_tag_pattern % changelog_future_release.to_s
395
+ config.exclude_labels = ['maintenance']
396
+ config.header = "# Change log\n\nAll notable changes to this project will be documented in this file. " \
397
+ 'The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres ' \
398
+ 'to [Semantic Versioning](https://semver.org).'
399
+ config.add_pr_wo_labels = true
400
+ config.issues = false
401
+ config.merge_prefix = '### UNCATEGORIZED PRS; GO LABEL THEM'
402
+ config.configure_sections = {
403
+ 'Changed' => {
404
+ 'prefix' => '### Changed',
405
+ 'labels' => ['backwards-incompatible'],
406
+ },
407
+ 'Added' => {
408
+ 'prefix' => '### Added',
409
+ 'labels' => %w[feature enhancement],
410
+ },
411
+ 'Fixed' => {
412
+ 'prefix' => '### Fixed',
413
+ 'labels' => ['bugfix'],
414
+ },
415
+ }
416
+ end
417
+ else
418
+ desc 'Generate a Changelog from GitHub'
419
+ task :changelog do
420
+ raise <<~MESSAGE
421
+ The changelog tasks depends on unreleased features of the github_changelog_generator gem.
422
+ Please manually add it to your .sync.yml for now, and run `pdk update`:
423
+ ---
424
+ Gemfile:
425
+ optional:
426
+ ':development':
427
+ - gem: 'github_changelog_generator'
428
+ git: 'https://github.com/skywinder/github-changelog-generator'
429
+ ref: '20ee04ba1234e9e83eb2ffb5056e23d641c7a018'
430
+ condition: "Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.2.2')"
431
+ MESSAGE
703
432
  end
704
- rescue Gem::LoadError
705
- puts "No gettext-setup gem found, skipping GettextSetup config initialization" if Rake.verbose == true
706
433
  end
707
434
  end