puppetlabs_spec_helper 2.14.1 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.github/dependabot.yml +15 -0
- data/.rubocop.yml +23 -6
- data/.rubocop_todo.yml +18 -18
- data/.travis.yml +14 -26
- data/CHANGELOG.md +87 -2
- data/CODEOWNERS +2 -0
- data/Gemfile +28 -10
- data/HISTORY.md +1 -1
- data/README.md +11 -34
- data/Rakefile +1 -1
- data/lib/puppetlabs_spec_helper/module_spec_helper.rb +10 -0
- data/lib/puppetlabs_spec_helper/puppet_spec_helper.rb +4 -2
- data/lib/puppetlabs_spec_helper/puppetlabs_spec/files.rb +2 -0
- data/lib/puppetlabs_spec_helper/puppetlabs_spec/fixtures.rb +7 -2
- data/lib/puppetlabs_spec_helper/puppetlabs_spec/matchers.rb +4 -2
- data/lib/puppetlabs_spec_helper/puppetlabs_spec/puppet_internals.rb +5 -2
- data/lib/puppetlabs_spec_helper/puppetlabs_spec_helper.rb +2 -0
- data/lib/puppetlabs_spec_helper/rake_tasks.rb +131 -78
- data/lib/puppetlabs_spec_helper/tasks/check_symlinks.rb +4 -1
- data/lib/puppetlabs_spec_helper/tasks/fixtures.rb +161 -91
- data/lib/puppetlabs_spec_helper/version.rb +3 -1
- data/puppet_spec_helper.rb +2 -0
- data/puppetlabs_spec_helper.gemspec +8 -7
- data/puppetlabs_spec_helper.rb +2 -0
- metadata +61 -50
- data/lib/puppetlabs_spec_helper/tasks/beaker.rb +0 -112
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'pathspec'
|
2
4
|
|
3
5
|
module PuppetlabsSpecHelper; end
|
@@ -33,7 +35,7 @@ class PuppetlabsSpecHelper::Tasks::CheckSymlinks
|
|
33
35
|
end
|
34
36
|
|
35
37
|
def ignored?(path)
|
36
|
-
path = path
|
38
|
+
path = "#{path}/" if File.directory?(path)
|
37
39
|
|
38
40
|
!ignore_pathspec.match_paths([path], Dir.pwd).empty?
|
39
41
|
end
|
@@ -42,6 +44,7 @@ class PuppetlabsSpecHelper::Tasks::CheckSymlinks
|
|
42
44
|
@ignore_pathspec ||= PathSpec.new(DEFAULT_IGNORED).tap do |pathspec|
|
43
45
|
IGNORE_LIST_FILES.each do |f|
|
44
46
|
next unless File.file?(f) && File.readable?(f)
|
47
|
+
|
45
48
|
File.open(f, 'r') { |fd| pathspec.add(fd) }
|
46
49
|
end
|
47
50
|
end
|
@@ -1,15 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'yaml'
|
2
4
|
require 'open3'
|
3
5
|
require 'json'
|
4
6
|
|
5
7
|
module PuppetlabsSpecHelper; end
|
6
8
|
module PuppetlabsSpecHelper::Tasks; end
|
9
|
+
|
7
10
|
module PuppetlabsSpecHelper::Tasks::FixtureHelpers
|
8
11
|
# This is a helper for the self-symlink entry of fixtures.yml
|
9
12
|
def source_dir
|
10
13
|
Dir.pwd
|
11
14
|
end
|
12
15
|
|
16
|
+
# @return [String] - the name of current module
|
13
17
|
def module_name
|
14
18
|
raise ArgumentError unless File.file?('metadata.json') && File.readable?('metadata.json')
|
15
19
|
|
@@ -23,16 +27,48 @@ module PuppetlabsSpecHelper::Tasks::FixtureHelpers
|
|
23
27
|
File.basename(Dir.pwd).split('-').last
|
24
28
|
end
|
25
29
|
|
26
|
-
|
30
|
+
def module_version(path)
|
31
|
+
metadata_path = File.join(path, 'metadata.json')
|
32
|
+
raise ArgumentError unless File.file?(metadata_path) && File.readable?(metadata_path)
|
33
|
+
|
34
|
+
metadata = JSON.parse(File.read(metadata_path))
|
35
|
+
metadata.fetch('version', nil) || '0.0.1'
|
36
|
+
rescue JSON::ParserError, ArgumentError
|
37
|
+
logger.warn "Failed to find module version at path #{path}"
|
38
|
+
'0.0.1'
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [Hash] - returns a hash of all the fixture repositories
|
42
|
+
# @example
|
43
|
+
# {"puppetlabs-stdlib"=>{"target"=>"https://gitlab.com/puppetlabs/puppet-stdlib.git",
|
44
|
+
# "ref"=>nil, "branch"=>"main", "scm"=>nil,
|
45
|
+
# }}
|
27
46
|
def repositories
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
47
|
+
@repositories ||= fixtures('repositories') || {}
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return [Hash] - returns a hash of all the fixture forge modules
|
51
|
+
# @example
|
52
|
+
# {"puppetlabs-stdlib"=>{"target"=>"spec/fixtures/modules/stdlib",
|
53
|
+
# "ref"=>nil, "branch"=>nil, "scm"=>nil,
|
54
|
+
# "flags"=>"--module_repository=https://myforge.example.com/", "subdir"=>nil}}
|
55
|
+
def forge_modules
|
56
|
+
@forge_modules ||= fixtures('forge_modules') || {}
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [Hash] - a hash of symlinks specified in the fixtures file
|
60
|
+
def symlinks
|
61
|
+
@symlinks ||= fixtures('symlinks') || {}
|
32
62
|
end
|
33
63
|
|
64
|
+
# @return [Hash] - returns a hash with the module name and the source directory
|
34
65
|
def auto_symlink
|
35
|
-
{ module_name =>
|
66
|
+
{ module_name => "\#{source_dir}" }
|
67
|
+
end
|
68
|
+
|
69
|
+
# @return [Boolean] - true if the os is a windows system
|
70
|
+
def windows?
|
71
|
+
!!File::ALT_SEPARATOR
|
36
72
|
end
|
37
73
|
|
38
74
|
def fixtures(category)
|
@@ -77,7 +113,6 @@ module PuppetlabsSpecHelper::Tasks::FixtureHelpers
|
|
77
113
|
|
78
114
|
result = {}
|
79
115
|
if fixtures.include?(category) && !fixtures[category].nil?
|
80
|
-
|
81
116
|
defaults = { 'target' => 'spec/fixtures/modules' }
|
82
117
|
|
83
118
|
# load defaults from the `.fixtures.yml` `defaults` section
|
@@ -94,28 +129,39 @@ module PuppetlabsSpecHelper::Tasks::FixtureHelpers
|
|
94
129
|
end
|
95
130
|
# there should be a warning or something if it's not a hash...
|
96
131
|
next unless opts.instance_of?(Hash)
|
132
|
+
|
97
133
|
# merge our options into the defaults to get the
|
98
134
|
# final option list
|
99
135
|
opts = defaults.merge(opts)
|
100
136
|
|
101
137
|
next unless include_repo?(opts['puppet_version'])
|
102
138
|
|
103
|
-
real_target = eval(
|
104
|
-
real_source = eval(
|
139
|
+
real_target = eval("\"#{opts['target']}\"", binding, __FILE__, __LINE__) # evaluating target reference in this context (see auto_symlink)
|
140
|
+
real_source = eval("\"#{opts['repo']}\"", binding, __FILE__, __LINE__) # evaluating repo reference in this context (see auto_symlink)
|
105
141
|
|
106
|
-
result[real_source] =
|
142
|
+
result[real_source] = validate_fixture_hash!(
|
107
143
|
'target' => File.join(real_target, fixture),
|
108
|
-
'ref' => opts['ref'],
|
144
|
+
'ref' => opts['ref'] || opts['tag'],
|
109
145
|
'branch' => opts['branch'],
|
110
146
|
'scm' => opts['scm'],
|
111
147
|
'flags' => opts['flags'],
|
112
148
|
'subdir' => opts['subdir'],
|
113
|
-
|
149
|
+
)
|
114
150
|
end
|
115
151
|
end
|
116
152
|
result
|
117
153
|
end
|
118
154
|
|
155
|
+
def validate_fixture_hash!(hash)
|
156
|
+
# Can only validate git based scm
|
157
|
+
return hash unless hash['scm'] == 'git'
|
158
|
+
|
159
|
+
# Forward slashes in the ref aren't allowed. And is probably a branch name.
|
160
|
+
raise ArgumentError, "The ref for #{hash['target']} is invalid (Contains a forward slash). If this is a branch name, please use the 'branch' setting instead." if hash['ref'] =~ %r{/}
|
161
|
+
|
162
|
+
hash
|
163
|
+
end
|
164
|
+
|
119
165
|
def include_repo?(version_range)
|
120
166
|
if version_range && defined?(SemanticPuppet)
|
121
167
|
puppet_spec = Gem::Specification.find_by_name('puppet')
|
@@ -149,6 +195,7 @@ module PuppetlabsSpecHelper::Tasks::FixtureHelpers
|
|
149
195
|
unless File.exist?(target)
|
150
196
|
raise "Failed to clone #{scm} repository #{remote} into #{target}"
|
151
197
|
end
|
198
|
+
|
152
199
|
result
|
153
200
|
end
|
154
201
|
|
@@ -196,7 +243,7 @@ module PuppetlabsSpecHelper::Tasks::FixtureHelpers
|
|
196
243
|
end
|
197
244
|
|
198
245
|
def git_remote_url(target)
|
199
|
-
output, status = Open3.capture2e('git', '-
|
246
|
+
output, status = Open3.capture2e('git', '--git-dir', File.join(target, '.git'), 'ls-remote', '--get-url', 'origin')
|
200
247
|
status.success? ? output.strip : nil
|
201
248
|
end
|
202
249
|
|
@@ -219,7 +266,7 @@ module PuppetlabsSpecHelper::Tasks::FixtureHelpers
|
|
219
266
|
else
|
220
267
|
Logger::INFO
|
221
268
|
end
|
222
|
-
@logger = Logger.new(
|
269
|
+
@logger = Logger.new($stderr)
|
223
270
|
@logger.level = level
|
224
271
|
end
|
225
272
|
@logger
|
@@ -229,12 +276,13 @@ module PuppetlabsSpecHelper::Tasks::FixtureHelpers
|
|
229
276
|
# The problem with the relative path is that PMT doesn't expand the path properly and so passing in a relative path here
|
230
277
|
# becomes something like C:\somewhere\backslashes/spec/fixtures/work-dir on Windows, and then PMT barfs itself.
|
231
278
|
# This has been reported as https://tickets.puppetlabs.com/browse/PUP-4884
|
232
|
-
File.expand_path(
|
279
|
+
File.expand_path(ENV['MODULE_WORKING_DIR'] || 'spec/fixtures/work-dir')
|
233
280
|
end
|
234
281
|
|
235
282
|
# returns the current thread count that is currently active
|
236
283
|
# a status of false or nil means the thread completed
|
237
284
|
# so when anything else we count that as a active thread
|
285
|
+
# @return [Integer] - current thread count
|
238
286
|
def current_thread_count(items)
|
239
287
|
active_threads = items.find_all do |_item, opts|
|
240
288
|
if opts[:thread]
|
@@ -247,79 +295,47 @@ module PuppetlabsSpecHelper::Tasks::FixtureHelpers
|
|
247
295
|
active_threads.count
|
248
296
|
end
|
249
297
|
|
250
|
-
#
|
251
|
-
#
|
298
|
+
# @summary Set a limit on the amount threads used, defaults to 10
|
299
|
+
# MAX_FIXTURE_THREAD_COUNT can be used to set this limit
|
300
|
+
# @return [Integer] - returns the max_thread_count
|
252
301
|
def max_thread_limit
|
253
|
-
|
254
|
-
# the default thread count is 10 but can be
|
255
|
-
# raised by using environment variable MAX_FIXTURE_THREAD_COUNT
|
256
|
-
@max_thread_limit = if ENV['MAX_FIXTURE_THREAD_COUNT'].to_i > 0
|
257
|
-
ENV['MAX_FIXTURE_THREAD_COUNT'].to_i
|
258
|
-
else
|
259
|
-
10 # the default
|
260
|
-
end
|
261
|
-
end
|
262
|
-
@max_thread_limit
|
302
|
+
@max_thread_limit ||= (ENV['MAX_FIXTURE_THREAD_COUNT'] || 10).to_i
|
263
303
|
end
|
264
|
-
end
|
265
|
-
include PuppetlabsSpecHelper::Tasks::FixtureHelpers
|
266
304
|
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
end
|
279
|
-
|
280
|
-
# git has a race condition creating that directory, that would lead to aborted clone operations
|
281
|
-
FileUtils.mkdir_p('spec/fixtures/modules')
|
282
|
-
|
283
|
-
repositories.each do |remote, opts|
|
284
|
-
scm = 'git'
|
285
|
-
target = opts['target']
|
286
|
-
subdir = opts['subdir']
|
287
|
-
ref = opts['ref']
|
288
|
-
scm = opts['scm'] if opts['scm']
|
289
|
-
branch = opts['branch'] if opts['branch']
|
290
|
-
flags = opts['flags']
|
291
|
-
# get the current active threads that are alive
|
292
|
-
count = current_thread_count(repositories)
|
293
|
-
if count < max_thread_limit
|
294
|
-
logger.debug "New Thread started for #{remote}"
|
295
|
-
# start up a new thread and store it in the opts hash
|
296
|
-
opts[:thread] = Thread.new do
|
297
|
-
if valid_repo?(scm, target, remote)
|
298
|
-
update_repo(scm, target)
|
299
|
-
else
|
300
|
-
clone_repo(scm, remote, target, subdir, ref, branch, flags)
|
305
|
+
# @param items [Hash] - a hash of either repositories or forge modules
|
306
|
+
# @param [Block] - the method you wish to use to download the item
|
307
|
+
def download_items(items)
|
308
|
+
items.each do |remote, opts|
|
309
|
+
# get the current active threads that are alive
|
310
|
+
count = current_thread_count(items)
|
311
|
+
if count < max_thread_limit
|
312
|
+
logger.debug "New Thread started for #{remote}"
|
313
|
+
# start up a new thread and store it in the opts hash
|
314
|
+
opts[:thread] = Thread.new do
|
315
|
+
yield(remote, opts)
|
301
316
|
end
|
302
|
-
|
303
|
-
|
317
|
+
else
|
318
|
+
# the last thread started should be the longest wait
|
319
|
+
item, item_opts = items.find_all { |_i, o| o.key?(:thread) }.last
|
320
|
+
logger.debug "Waiting on #{item}"
|
321
|
+
item_opts[:thread].join # wait for the thread to finish
|
322
|
+
# now that we waited lets try again
|
323
|
+
redo
|
304
324
|
end
|
305
|
-
else
|
306
|
-
# the last thread started should be the longest wait
|
307
|
-
item, item_opts = repositories.find_all { |_i, o| o.key?(:thread) }.last
|
308
|
-
logger.debug "Waiting on #{item}"
|
309
|
-
item_opts[:thread].join # wait for the thread to finish
|
310
|
-
# now that we waited lets try again
|
311
|
-
redo
|
312
325
|
end
|
326
|
+
# wait for all the threads to finish
|
327
|
+
items.each { |_remote, opts| opts[:thread].join }
|
313
328
|
end
|
314
329
|
|
315
|
-
#
|
316
|
-
|
317
|
-
|
318
|
-
|
330
|
+
# @param target [String] - the target directory
|
331
|
+
# @param link [String] - the name of the link you wish to create
|
332
|
+
# works on windows and linux
|
333
|
+
def setup_symlink(target, link)
|
319
334
|
link = link['target']
|
320
|
-
|
335
|
+
return if File.symlink?(link)
|
336
|
+
|
321
337
|
logger.info("Creating symlink from #{link} to #{target}")
|
322
|
-
if
|
338
|
+
if windows?
|
323
339
|
target = File.join(File.dirname(link), target) unless Pathname.new(target).absolute?
|
324
340
|
if Dir.respond_to?(:create_junction)
|
325
341
|
Dir.create_junction(link, target)
|
@@ -331,7 +347,35 @@ task :spec_prep do
|
|
331
347
|
end
|
332
348
|
end
|
333
349
|
|
334
|
-
|
350
|
+
# @return [Boolean] - returns true if the module was downloaded successfully, false otherwise
|
351
|
+
# @param [String] - the remote url or namespace/name of the module to download
|
352
|
+
# @param [Hash] - list of options such as version, branch, ref
|
353
|
+
def download_repository(remote, opts)
|
354
|
+
scm = 'git'
|
355
|
+
target = opts['target']
|
356
|
+
subdir = opts['subdir']
|
357
|
+
ref = opts['ref']
|
358
|
+
scm = opts['scm'] if opts['scm']
|
359
|
+
branch = opts['branch'] if opts['branch']
|
360
|
+
flags = opts['flags']
|
361
|
+
if valid_repo?(scm, target, remote)
|
362
|
+
update_repo(scm, target)
|
363
|
+
else
|
364
|
+
clone_repo(scm, remote, target, subdir, ref, branch, flags)
|
365
|
+
end
|
366
|
+
revision(scm, target, ref) if ref
|
367
|
+
remove_subdirectory(target, subdir) if subdir
|
368
|
+
end
|
369
|
+
|
370
|
+
# @return [String] - the spec/fixtures/modules directory in the module root folder
|
371
|
+
def module_target_dir
|
372
|
+
@module_target_dir ||= File.expand_path('spec/fixtures/modules')
|
373
|
+
end
|
374
|
+
|
375
|
+
# @return [Boolean] - returns true if the module was downloaded successfully, false otherwise
|
376
|
+
# @param [String] - the remote url or namespace/name of the module to download
|
377
|
+
# @param [Hash] - list of options such as version
|
378
|
+
def download_module(remote, opts)
|
335
379
|
ref = ''
|
336
380
|
flags = ''
|
337
381
|
if opts.instance_of?(String)
|
@@ -342,34 +386,60 @@ task :spec_prep do
|
|
342
386
|
flags = " #{opts['flags']}" if opts['flags']
|
343
387
|
end
|
344
388
|
|
345
|
-
|
389
|
+
return false if File.directory?(target) && (ref.empty? || opts['ref'] == module_version(target))
|
390
|
+
|
391
|
+
# The PMT cannot handle multi threaded runs due to cache directory collisons
|
392
|
+
# so we randomize the directory instead.
|
393
|
+
# Does working_dir even need to be passed?
|
394
|
+
Dir.mktmpdir do |working_dir|
|
395
|
+
command = "puppet module install#{ref}#{flags} --ignore-dependencies" \
|
396
|
+
' --force' \
|
397
|
+
" --module_working_dir \"#{working_dir}\"" \
|
398
|
+
" --target-dir \"#{module_target_dir}\" \"#{remote}\""
|
346
399
|
|
347
|
-
|
348
|
-
|
400
|
+
unless system(command)
|
401
|
+
raise "Failed to install module #{remote} to #{module_target_dir}"
|
402
|
+
end
|
403
|
+
end
|
404
|
+
$CHILD_STATUS.success?
|
405
|
+
end
|
406
|
+
end
|
349
407
|
|
350
|
-
|
351
|
-
' --ignore-dependencies' \
|
352
|
-
' --force' \
|
353
|
-
" --module_working_dir \"#{working_dir}\"" \
|
354
|
-
" --target-dir \"#{target_dir}\" \"#{remote}\""
|
408
|
+
include PuppetlabsSpecHelper::Tasks::FixtureHelpers # DSL include # rubocop:disable Style/MixinUsage
|
355
409
|
|
356
|
-
|
357
|
-
|
410
|
+
desc 'Create the fixtures directory'
|
411
|
+
task :spec_prep do
|
412
|
+
# Ruby only sets File::ALT_SEPARATOR on Windows and Rubys standard library
|
413
|
+
# uses this to check for Windows
|
414
|
+
if windows?
|
415
|
+
begin
|
416
|
+
require 'win32/dir'
|
417
|
+
rescue LoadError
|
418
|
+
warn 'win32-dir gem not installed, falling back to executing mklink directly'
|
358
419
|
end
|
359
420
|
end
|
360
421
|
|
422
|
+
# git has a race condition creating that directory, that would lead to aborted clone operations
|
423
|
+
FileUtils.mkdir_p('spec/fixtures/modules')
|
424
|
+
|
425
|
+
symlinks.each { |target, link| setup_symlink(target, link) }
|
426
|
+
|
427
|
+
download_items(repositories) { |remote, opts| download_repository(remote, opts) }
|
428
|
+
|
429
|
+
download_items(forge_modules) { |remote, opts| download_module(remote, opts) }
|
430
|
+
|
361
431
|
FileUtils.mkdir_p('spec/fixtures/manifests')
|
362
432
|
FileUtils.touch('spec/fixtures/manifests/site.pp')
|
363
433
|
end
|
364
434
|
|
365
435
|
desc 'Clean up the fixtures directory'
|
366
436
|
task :spec_clean do
|
367
|
-
|
437
|
+
repositories.each do |_remote, opts|
|
368
438
|
target = opts['target']
|
369
439
|
FileUtils.rm_rf(target)
|
370
440
|
end
|
371
441
|
|
372
|
-
|
442
|
+
forge_modules.each do |_remote, opts|
|
373
443
|
target = opts['target']
|
374
444
|
FileUtils.rm_rf(target)
|
375
445
|
end
|
data/puppet_spec_helper.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
lib = File.expand_path('
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
5
|
require 'puppetlabs_spec_helper/version'
|
6
6
|
|
@@ -20,18 +20,19 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
21
|
spec.require_paths = ['lib']
|
22
22
|
|
23
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 2.4')
|
24
|
+
|
23
25
|
spec.add_runtime_dependency 'mocha', '~> 1.0'
|
26
|
+
spec.add_runtime_dependency 'pathspec', '>= 0.2.1', '< 1.1.0'
|
24
27
|
spec.add_runtime_dependency 'puppet-lint', '~> 2.0'
|
25
|
-
spec.add_runtime_dependency 'puppet-syntax', '
|
28
|
+
spec.add_runtime_dependency 'puppet-syntax', ['>= 2.0', '< 4']
|
26
29
|
spec.add_runtime_dependency 'rspec-puppet', '~> 2.0'
|
27
|
-
spec.add_runtime_dependency 'pathspec', '~> 0.2.1'
|
28
30
|
|
29
31
|
spec.add_development_dependency 'bundler'
|
32
|
+
spec.add_development_dependency 'fakefs', ['>= 0.13.3', '< 2']
|
30
33
|
spec.add_development_dependency 'pry'
|
31
34
|
spec.add_development_dependency 'puppet'
|
32
|
-
spec.add_development_dependency 'rake', '
|
35
|
+
spec.add_development_dependency 'rake', ['>= 10.0', '< 14']
|
33
36
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
34
37
|
spec.add_development_dependency 'yard'
|
35
|
-
spec.add_development_dependency 'gettext-setup', '~> 0.29'
|
36
|
-
spec.add_development_dependency 'fakefs', '~> 0.13.3'
|
37
38
|
end
|