puppetlabs_spec_helper 2.6.2 → 4.0.1

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