File: rake_tasks.rb

package info (click to toggle)
ruby-puppetlabs-spec-helper 8.0.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 344 kB
  • sloc: ruby: 1,526; sh: 8; makefile: 6
file content (383 lines) | stat: -rw-r--r-- 12,495 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
# frozen_string_literal: true

require 'fileutils'
require 'rake'
require 'rspec/core/rake_task'
require 'tmpdir'
require 'pathname'
require 'puppetlabs_spec_helper/version'
require 'puppetlabs_spec_helper/tasks/fixtures'
require 'puppetlabs_spec_helper/tasks/check_symlinks'
require 'English'

# optional gems
begin
  require 'metadata-json-lint/rake_task'
rescue LoadError
  # ignore
end

begin
  require 'puppet_blacksmith/rake_tasks'
rescue LoadError
  # ignore
end

begin
  require 'github_changelog_generator/task'
rescue LoadError
  # ignore
end

begin
  require 'puppet-strings/tasks'
rescue LoadError
  # ignore
end

parallel_tests_loaded = false
begin
  require 'parallel_tests'
  parallel_tests_loaded = true
rescue LoadError
  # ignore
end

task default: [:help]

pattern = 'spec/{aliases,classes,defines,functions,hosts,integration,plans,tasks,type_aliases,types,unit}/**/*_spec.rb'

RSpec::Core::RakeTask.new(:spec_standalone) do |t, args|
  t.rspec_opts = []
  t.rspec_opts << ENV['CI_SPEC_OPTIONS'] unless ENV['CI_SPEC_OPTIONS'].nil?
  if ENV['CI_NODE_TOTAL'] && ENV['CI_NODE_INDEX']
    ci_total = ENV['CI_NODE_TOTAL'].to_i
    ci_index = ENV['CI_NODE_INDEX'].to_i
    raise "CI_NODE_INDEX must be between 1-#{ci_total}" unless ci_index >= 1 && ci_index <= ci_total

    files = Rake::FileList[pattern].to_a
    per_node = (files.size / ci_total.to_f).ceil
    t.pattern = if args.extras.nil? || args.extras.empty?
                  files.each_slice(per_node).to_a[ci_index - 1] || files.first
                else
                  args.extras.join(',')
                end
  else
    t.pattern = if args.extras.nil? || args.extras.empty?
                  pattern
                else
                  args.extras.join(',')
                end
  end
end

desc 'List spec tests in a JSON document'
RSpec::Core::RakeTask.new(:spec_list_json) do |t|
  t.rspec_opts = ['--dry-run', '--format', 'json']
  t.pattern = pattern
end

desc 'Run spec tests and clean the fixtures directory if successful'
task :spec do |_t, args|
  Rake::Task[:spec_prep].invoke
  Rake::Task[:spec_standalone].invoke(*args.extras)
  Rake::Task[:spec_clean].invoke
ensure
  Rake::Task[:spec_clean_symlinks].invoke
end

desc 'Run spec tests with ruby simplecov code coverage'
namespace :spec do
  task :simplecov do
    ENV['SIMPLECOV'] = 'yes'
    Rake::Task['spec'].execute
  end
end

desc 'Run spec tests in parallel and clean the fixtures directory if successful'
task :parallel_spec do |_t, args|
  Rake::Task[:spec_prep].invoke
  Rake::Task[:parallel_spec_standalone].invoke(*args.extras)
  Rake::Task[:spec_clean].invoke
ensure
  Rake::Task[:spec_clean_symlinks].invoke
end

desc 'Parallel spec tests'
task :parallel_spec_standalone do |_t, args|
  raise 'Add the parallel_tests gem to Gemfile to enable this task' unless parallel_tests_loaded

  if Rake::FileList[pattern].to_a.empty?
    warn 'No files for parallel_spec to run against'
  else

    args = ['--type', 'rspec']
    additional_options = ['--format', 'progress'] + ENV['CI_SPEC_OPTIONS'].to_s.strip.split
    args.push('--').concat(additional_options).push('--')
    args.concat(Rake::FileList[pattern].to_a)

    ParallelTests::CLI.new.run(args)

  end
end

require 'puppet-lint/tasks/puppet-lint'
# Must clear as it will not override the existing puppet-lint rake task since we require to import for
# the PuppetLint::RakeTask
Rake::Task[:lint].clear
PuppetLint.configuration.fail_on_warnings = true
# Utilize PuppetLint global configuration so that these settings can be tweaked by
# spec_helper.rb in an individual module
PuppetLint.configuration.relative = true
PuppetLint.configuration.ignore_paths ||= []
PuppetLint.configuration.ignore_paths << '.vendor/**/*.pp'
PuppetLint.configuration.ignore_paths << 'bundle/**/*.pp'
PuppetLint.configuration.ignore_paths << 'pkg/**/*.pp'
PuppetLint.configuration.ignore_paths << 'spec/**/*.pp'
PuppetLint.configuration.ignore_paths << 'tests/**/*.pp'
PuppetLint.configuration.ignore_paths << 'types/**/*.pp'
PuppetLint.configuration.ignore_paths << 'vendor/**/*.pp'
puppet_lint_disable_checks = %w[
  80chars
  140chars
  class_inherits_from_params_class
  disable_autoloader_layout
  documentation
  single_quote_string_with_variables
]

puppet_lint_disable_checks.each do |check|
  PuppetLint.configuration.send(:"disable_#{check}")
end
PuppetLint::RakeTask.new(:lint)

desc 'Run puppet-lint and fix issues automatically'
PuppetLint::RakeTask.new(:lint_fix) do |config|
  config.fix = true
end

require 'puppet-syntax/tasks/puppet-syntax'
PuppetSyntax.exclude_paths ||= []
PuppetSyntax.exclude_paths << 'spec/fixtures/**/*'
PuppetSyntax.exclude_paths << 'pkg/**/*'
PuppetSyntax.exclude_paths << 'vendor/**/*'
PuppetSyntax.exclude_paths << '.vendor/**/*'
PuppetSyntax.check_hiera_keys = true
PuppetSyntax.check_hiera_data = true

desc 'Check syntax of Ruby files and call :syntax and :metadata_lint'
task :validate do
  Dir['lib/**/*.rb'].each do |lib_file|
    sh "ruby -c #{lib_file}"
  end

  Rake::Task[:syntax].invoke
  if File.exist?('metadata.json')
    if Rake::Task.task_defined?(:metadata_lint)
      Rake::Task[:metadata_lint].invoke
    else
      warn 'Skipping metadata validation; the metadata-json-lint gem was not found'
    end
  end

  if File.exist?('REFERENCE.md')
    if Rake::Task.task_defined?('strings:validate:reference')
      Rake::Task['strings:validate:reference'].invoke
    else
      warn 'Skipping reference documentation validation; the puppet-strings gem was not found'
    end
  end
end

desc 'Print development version of module'
task :compute_dev_version do
  version = ''
  if File.exist?('metadata.json')
    require 'json'

    modinfo = JSON.parse(File.read('metadata.json'))
    version = modinfo['version']
  elsif File.exist?('Modulefile')
    modfile = File.read('Modulefile')
    version = modfile.match(/\nversion +['"](.*)['"]/)[1]
  else
    raise 'Could not find a metadata.json or Modulefile! Cannot compute dev version without one or the other!'
  end

  sha = `git rev-parse HEAD`[0..7]
  branch = `git rev-parse --abbrev-ref HEAD`

  # If we're in a CI environment include our build number
  # If the branch is a release branch we append an 'r' into the new_version,
  # this is due to the release branch buildID conflicting with main branch when trying to push to the staging forge.
  # More info can be found at https://tickets.puppetlabs.com/browse/FM-6170
  new_version = if (build = ENV.fetch('BUILD_NUMBER', nil))
                  if branch.eql? 'release'
                    format('%s-%s%04d-%s', version, 'r', build, sha) # legacy support code
                  else
                    format('%s-%04d-%s', version, build, sha) # legacy support code
                  end
                else
                  "#{version}-#{sha}"
                end

  print new_version
end

desc 'Runs all necessary checks on a module in preparation for a release'
task :release_checks do
  Rake::Task[:lint].invoke
  Rake::Task[:validate].invoke
  if parallel_tests_loaded
    Rake::Task[:parallel_spec].invoke
  else
    Rake::Task[:spec].invoke
  end
  Rake::Task[:check].invoke
end

namespace :check do
  desc 'Fails if symlinks are present in directory'
  task :symlinks do
    symlinks = PuppetlabsSpecHelper::Tasks::CheckSymlinks.new.check
    unless symlinks.empty?
      symlinks.each { |r| puts "Symlink found: #{r} => #{r.readlink}" }
      raise 'Symlink(s) exist within this directory'
    end
  end

  desc 'Fails if .pp files present in tests folder'
  task :test_file do
    ppfiles = Dir[File.join('tests', '**', '*.pp')]
    unless ppfiles.empty?
      puts ppfiles
      raise '.pp files present in tests folder; Move them to an examples folder following the new convention'
    end
  end

  desc 'Fails if any ._ files are present in directory'
  task :dot_underscore do
    dirs = Dir['._*']
    unless dirs.empty?
      puts dirs
      raise '._ files are present in the directory'
    end
  end

  desc 'Fails if directories contain the files specified in .gitignore'
  task :git_ignore do
    matched = `git ls-files --ignored --exclude-standard --cached`
    raise 'git ls-files failed' unless $CHILD_STATUS.success?

    unless matched == ''
      puts matched
      raise 'File specified in .gitignore has been committed'
    end
  end
end

desc 'Run static pre release checks'
task check: ['check:symlinks', 'check:test_file', 'check:dot_underscore', 'check:git_ignore']

desc 'Display the list of available rake tasks'
task :help do
  system('rake -T')
end

begin
  require 'rubocop/rake_task'
  RuboCop::RakeTask.new(:rubocop) do |task|
    # These make the rubocop experience maybe slightly less terrible
    task.options = ['-D', '-S', '-E']

    # Use Rubocop's Github Actions formatter if possible
    if ENV['GITHUB_ACTIONS'] == 'true'
      rubocop_spec = Gem::Specification.find_by_name('rubocop')
      task.formatters << 'github' if Gem::Version.new(rubocop_spec.version) >= Gem::Version.new('1.2')
    end
  end
rescue LoadError
  desc 'rubocop is not available in this installation'
  task :rubocop do
    raise 'rubocop is not available in this installation'
  end
end

def create_gch_task(changelog_user = nil, changelog_project = nil, changelog_since_tag = nil, changelog_tag_pattern = 'v%s')
  if Bundler.rubygems.find_name('github_changelog_generator').any?
    # needed a place to hide these methods
    # rubocop:disable Lint/NestedMethodDefinition
    def changelog_user_from_metadata
      result = JSON.parse(File.read('metadata.json'))['author']
      raise 'unable to find the changelog_user in .sync.yml, or the author in metadata.json' if result.nil?

      puts "GitHubChangelogGenerator user:#{result}"
      result
    end

    def changelog_project_from_metadata
      result = JSON.parse(File.read('metadata.json'))['name']
      raise 'unable to find the changelog_project in .sync.yml or the name in metadata.json' if result.nil?

      puts "GitHubChangelogGenerator project:#{result}"
      result
    end

    def changelog_future_release
      return unless Rake.application.top_level_tasks.include? 'changelog'

      result = JSON.parse(File.read('metadata.json'))['version']
      raise 'unable to find the future_release (version) in metadata.json' if result.nil?

      puts "GitHubChangelogGenerator future_release:#{result}"
      result
    end
    # rubocop:enable Lint/NestedMethodDefinition

    GitHubChangelogGenerator::RakeTask.new :changelog do |config|
      raise "Set CHANGELOG_GITHUB_TOKEN environment variable eg 'export CHANGELOG_GITHUB_TOKEN=valid_token_here'" if ENV['CHANGELOG_GITHUB_TOKEN'].nil?

      config.user = changelog_user || changelog_user_from_metadata
      config.project = changelog_project || changelog_project_from_metadata
      config.since_tag = changelog_since_tag if changelog_since_tag
      config.future_release = changelog_tag_pattern % changelog_future_release.to_s
      config.exclude_labels = ['maintenance']
      config.header = "# Change log\n\nAll notable changes to this project will be documented in this file. " \
                      'The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres ' \
                      'to [Semantic Versioning](https://semver.org).'
      config.add_pr_wo_labels = true
      config.issues = false
      config.merge_prefix = '### UNCATEGORIZED PRS; GO LABEL THEM'
      config.configure_sections = {
        'Changed' => {
          'prefix' => '### Changed',
          'labels' => ['backwards-incompatible'],
        },
        'Added' => {
          'prefix' => '### Added',
          'labels' => %w[feature enhancement],
        },
        'Fixed' => {
          'prefix' => '### Fixed',
          'labels' => ['bugfix'],
        },
      }
    end
  else
    desc 'Generate a Changelog from GitHub'
    task :changelog do
      raise <<~MESSAGE
        The changelog tasks depends on unreleased features of the github_changelog_generator gem.
        Please manually add it to your .sync.yml for now, and run `pdk update`:
        ---
        Gemfile:
          optional:
            ':development':
              - gem: 'github_changelog_generator'
                git: 'https://github.com/skywinder/github-changelog-generator'
                ref: '20ee04ba1234e9e83eb2ffb5056e23d641c7a018'
                condition: "Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.2.2')"
      MESSAGE
    end
  end
end