puppetlabs_spec_helper 5.0.1 → 5.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dd628239ec2911772d37af4b47553d83c1ebebbc7a5fa0dc61d89d170fb513e4
4
- data.tar.gz: 66bd38e002986eb1921889db2934971936eba7936d0056bf6f6ac88c8745b901
3
+ metadata.gz: '0463812df931c62b101e5dd8711923e5787b75710b75a9cd22e965db859f7ca4'
4
+ data.tar.gz: 1fed48c27d1f6e64a37d127866158933b8188eb09241d66c2587e54b7ff3e358
5
5
  SHA512:
6
- metadata.gz: fe79780dbf5b0d270c5426222b136989c1c6af14d934fc4449a7be573e986a73e42d3c5799f522afaa976c27d0397646f2517e520d72de83ed22eed84ceb962e
7
- data.tar.gz: 57ee6e2ec746a210265fa7162eda41dfff212f055b47b9728caf38b651ecf959759d9f178d348e8f73e52ac75e705dc3aeb0a3edbb8edf7ab8f5609c28541938
6
+ metadata.gz: 6932eaa811fabd5fbd61b0447719f0dc51f1f69cd62e336f63dedf35c8fab6ee29bbb5fc004c65602edac33dab2e207bc08dbd56546dcdfefd18c52e1b95138c
7
+ data.tar.gz: c187e3756855b13ee5098505f892c143cfbd13d3301c50bea80ad52dd4a9e5f8419786a579da02dd15ba1ba5df2bd353ce6fc5906f2d402097a167c6331f7f8f
data/.rubocop.yml CHANGED
@@ -36,9 +36,6 @@ Style/BlockDelimiters:
36
36
  Description: Prefer braces for chaining. Mostly an aesthetical choice. Better to
37
37
  be consistent then.
38
38
  EnforcedStyle: braces_for_chaining
39
- Style/ClassAndModuleChildren:
40
- Description: Compact style reduces the required amount of indentation.
41
- EnforcedStyle: compact
42
39
  Style/EmptyElse:
43
40
  Description: Enforce against empty else clauses, but allow `nil` for clarity.
44
41
  EnforcedStyle: empty
@@ -6,56 +6,58 @@ require 'fileutils'
6
6
  require 'tempfile'
7
7
  require 'pathname'
8
8
 
9
- # A support module for testing files.
10
- module PuppetlabsSpec::Files
11
- # This code exists only to support tests that run as root, pretty much.
12
- # Once they have finally been eliminated this can all go... --daniel 2011-04-08
13
- def self.in_tmp(path)
14
- tempdir = Dir.tmpdir
15
-
16
- Pathname.new(path).ascend do |dir|
17
- return true if File.identical?(tempdir, dir)
18
- end
9
+ module PuppetlabsSpec
10
+ # A support module for testing files.
11
+ module Files
12
+ # This code exists only to support tests that run as root, pretty much.
13
+ # Once they have finally been eliminated this can all go... --daniel 2011-04-08
14
+ def self.in_tmp(path)
15
+ tempdir = Dir.tmpdir
16
+
17
+ Pathname.new(path).ascend do |dir|
18
+ return true if File.identical?(tempdir, dir)
19
+ end
19
20
 
20
- false
21
- end
21
+ false
22
+ end
22
23
 
23
- def self.cleanup
24
- $global_tempfiles ||= []
25
- while (path = $global_tempfiles.pop)
26
- raise "Not deleting tmpfile #{path} outside regular tmpdir" unless in_tmp(path)
24
+ def self.cleanup
25
+ $global_tempfiles ||= []
26
+ while (path = $global_tempfiles.pop)
27
+ raise "Not deleting tmpfile #{path} outside regular tmpdir" unless in_tmp(path)
27
28
 
28
- begin
29
- FileUtils.rm_r path, secure: true
30
- rescue Errno::ENOENT
31
- # nothing to do
29
+ begin
30
+ FileUtils.rm_r path, secure: true
31
+ rescue Errno::ENOENT
32
+ # nothing to do
33
+ end
32
34
  end
33
35
  end
34
- end
35
36
 
36
- def make_absolute(path)
37
- path = File.expand_path(path)
38
- path[0] = 'c' if Puppet.features.microsoft_windows?
39
- path
40
- end
37
+ def make_absolute(path)
38
+ path = File.expand_path(path)
39
+ path[0] = 'c' if Puppet.features.microsoft_windows?
40
+ path
41
+ end
41
42
 
42
- def tmpfilename(name)
43
- # Generate a temporary file, just for the name...
44
- source = Tempfile.new(name)
45
- path = source.path
46
- source.close!
43
+ def tmpfilename(name)
44
+ # Generate a temporary file, just for the name...
45
+ source = Tempfile.new(name)
46
+ path = source.path
47
+ source.close!
47
48
 
48
- # ...record it for cleanup,
49
- $global_tempfiles ||= []
50
- $global_tempfiles << File.expand_path(path)
49
+ # ...record it for cleanup,
50
+ $global_tempfiles ||= []
51
+ $global_tempfiles << File.expand_path(path)
51
52
 
52
- # ...and bam.
53
- path
54
- end
53
+ # ...and bam.
54
+ path
55
+ end
55
56
 
56
- def tmpdir(name)
57
- path = tmpfilename(name)
58
- FileUtils.mkdir_p(path)
59
- path
57
+ def tmpdir(name)
58
+ path = tmpfilename(name)
59
+ FileUtils.mkdir_p(path)
60
+ path
61
+ end
60
62
  end
61
63
  end
@@ -1,53 +1,55 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # This module provides some helper methods to assist with fixtures. It's
4
- # methods are designed to help when you have a conforming fixture layout so we
5
- # get project consistency.
6
- module PuppetlabsSpec::Fixtures
7
- # Returns the joined path of the global FIXTURE_DIR plus any path given to it
8
- def fixtures(*rest)
9
- File.join(PuppetlabsSpec::FIXTURE_DIR, *rest)
10
- end
11
-
12
- # Returns the path to your relative fixture dir. So if your spec test is
13
- # <project>/spec/unit/facter/foo_spec.rb then your relative dir will be
14
- # <project>/spec/fixture/unit/facter/foo
15
- def my_fixture_dir
16
- callers = caller
17
- while (line = callers.shift)
18
- next unless (found = line.match(%r{/spec/(.*)_spec\.rb:}))
19
-
20
- return fixtures(found[1])
3
+ module PuppetlabsSpec
4
+ # This module provides some helper methods to assist with fixtures. It's
5
+ # methods are designed to help when you have a conforming fixture layout so we
6
+ # get project consistency.
7
+ module Fixtures
8
+ # Returns the joined path of the global FIXTURE_DIR plus any path given to it
9
+ def fixtures(*rest)
10
+ File.join(PuppetlabsSpec::FIXTURE_DIR, *rest)
21
11
  end
22
- raise "sorry, I couldn't work out your path from the caller stack!"
23
- end
24
12
 
25
- # Given a name, returns the full path of a file from your relative fixture
26
- # dir as returned by my_fixture_dir.
27
- def my_fixture(name)
28
- file = File.join(my_fixture_dir, name)
29
- unless File.readable? file
30
- raise "fixture '#{name}' for #{my_fixture_dir} is not readable"
13
+ # Returns the path to your relative fixture dir. So if your spec test is
14
+ # <project>/spec/unit/facter/foo_spec.rb then your relative dir will be
15
+ # <project>/spec/fixture/unit/facter/foo
16
+ def my_fixture_dir
17
+ callers = caller
18
+ while (line = callers.shift)
19
+ next unless (found = line.match(%r{/spec/(.*)_spec\.rb:}))
20
+
21
+ return fixtures(found[1])
22
+ end
23
+ raise "sorry, I couldn't work out your path from the caller stack!"
31
24
  end
32
25
 
33
- file
34
- end
26
+ # Given a name, returns the full path of a file from your relative fixture
27
+ # dir as returned by my_fixture_dir.
28
+ def my_fixture(name)
29
+ file = File.join(my_fixture_dir, name)
30
+ unless File.readable? file
31
+ raise "fixture '#{name}' for #{my_fixture_dir} is not readable"
32
+ end
35
33
 
36
- # Return the contents of the file using read when given a name. Uses
37
- # my_fixture to work out the relative path.
38
- def my_fixture_read(name)
39
- File.read(my_fixture(name))
40
- end
34
+ file
35
+ end
41
36
 
42
- # Provides a block mechanism for iterating across the files in your fixture
43
- # area.
44
- def my_fixtures(glob = '*', flags = 0, &block)
45
- files = Dir.glob(File.join(my_fixture_dir, glob), flags)
46
- if files.empty?
47
- raise "fixture '#{glob}' for #{my_fixture_dir} had no files!"
37
+ # Return the contents of the file using read when given a name. Uses
38
+ # my_fixture to work out the relative path.
39
+ def my_fixture_read(name)
40
+ File.read(my_fixture(name))
48
41
  end
49
42
 
50
- block && files.each(&block)
51
- files
43
+ # Provides a block mechanism for iterating across the files in your fixture
44
+ # area.
45
+ def my_fixtures(glob = '*', flags = 0, &block)
46
+ files = Dir.glob(File.join(my_fixture_dir, glob), flags)
47
+ if files.empty?
48
+ raise "fixture '#{glob}' for #{my_fixture_dir} had no files!"
49
+ end
50
+
51
+ block && files.each(&block)
52
+ files
53
+ end
52
54
  end
53
55
  end
@@ -4,41 +4,43 @@
4
4
  # 'puppetlabs_spec_helper/puppet_spec_helper' library
5
5
  require 'puppetlabs_spec_helper/puppet_spec_helper'
6
6
 
7
- # PuppetInternals provides a set of methods that interface
8
- # with internal puppet implementations.
9
- module PuppetlabsSpec::PuppetInternals
10
- def resource(parts = {})
11
- resource_type = parts[:type] || :hostclass
12
- resource_name = parts[:name] || 'testing'
13
- Puppet::Resource::Type.new(resource_type, resource_name)
14
- end
15
- module_function :resource
7
+ module PuppetlabsSpec
8
+ # PuppetInternals provides a set of methods that interface
9
+ # with internal puppet implementations.
10
+ module PuppetInternals
11
+ def resource(parts = {})
12
+ resource_type = parts[:type] || :hostclass
13
+ resource_name = parts[:name] || 'testing'
14
+ Puppet::Resource::Type.new(resource_type, resource_name)
15
+ end
16
+ module_function :resource
16
17
 
17
- def compiler(parts = {})
18
- compiler_node = parts[:node] || node
19
- Puppet::Parser::Compiler.new(compiler_node)
20
- end
21
- module_function :compiler
18
+ def compiler(parts = {})
19
+ compiler_node = parts[:node] || node
20
+ Puppet::Parser::Compiler.new(compiler_node)
21
+ end
22
+ module_function :compiler
22
23
 
23
- def node(parts = {})
24
- node_name = parts[:name] || 'testinghost'
25
- options = parts[:options] || {}
26
- node_environment = Puppet::Node::Environment.create(parts[:environment] || 'test', [])
27
- options[:environment] = node_environment
28
- Puppet::Node.new(node_name, options)
29
- end
30
- module_function :node
24
+ def node(parts = {})
25
+ node_name = parts[:name] || 'testinghost'
26
+ options = parts[:options] || {}
27
+ node_environment = Puppet::Node::Environment.create(parts[:environment] || 'test', [])
28
+ options[:environment] = node_environment
29
+ Puppet::Node.new(node_name, options)
30
+ end
31
+ module_function :node
31
32
 
32
- # Return a method instance for a given function. This is primarily useful
33
- # for rspec-puppet
34
- def function_method(name, parts = {})
35
- scope = parts[:scope] || scope()
36
- # Ensure the method instance is defined by side-effect of checking if it
37
- # exists. This is a hack, but at least it's a hidden hack and not an
38
- # exposed hack.
39
- return nil unless Puppet::Parser::Functions.function(name)
33
+ # Return a method instance for a given function. This is primarily useful
34
+ # for rspec-puppet
35
+ def function_method(name, parts = {})
36
+ scope = parts[:scope] || scope()
37
+ # Ensure the method instance is defined by side-effect of checking if it
38
+ # exists. This is a hack, but at least it's a hidden hack and not an
39
+ # exposed hack.
40
+ return nil unless Puppet::Parser::Functions.function(name)
40
41
 
41
- scope.method("function_#{name}".to_sym)
42
+ scope.method("function_#{name}".to_sym)
43
+ end
44
+ module_function :function_method
42
45
  end
43
- module_function :function_method
44
46
  end
@@ -2,48 +2,52 @@
2
2
 
3
3
  require 'pathspec'
4
4
 
5
- # Helpers for validating symlinks.
6
- class PuppetlabsSpecHelper::Tasks::CheckSymlinks
7
- DEFAULT_IGNORED = [
8
- '/.git/',
9
- '/.bundle/',
10
- '/vendor/',
11
- ].freeze
12
-
13
- IGNORE_LIST_FILES = [
14
- '.pdkignore',
15
- '.gitignore',
16
- ].freeze
17
-
18
- def check(dir = Dir.pwd)
19
- dir = Pathname.new(dir) unless dir.is_a?(Pathname)
20
- results = []
21
-
22
- dir.each_child(true) do |child|
23
- next if ignored?(child.to_s)
24
-
25
- if child.symlink?
26
- results << child
27
- elsif child.directory? && child.basename.to_s !~ %r{^(\.git|\.?bundle)$}
28
- results.concat(check(child))
5
+ module PuppetlabsSpecHelper
6
+ module Tasks
7
+ # Helpers for validating symlinks.
8
+ class CheckSymlinks
9
+ DEFAULT_IGNORED = [
10
+ '/.git/',
11
+ '/.bundle/',
12
+ '/vendor/',
13
+ ].freeze
14
+
15
+ IGNORE_LIST_FILES = [
16
+ '.pdkignore',
17
+ '.gitignore',
18
+ ].freeze
19
+
20
+ def check(dir = Dir.pwd)
21
+ dir = Pathname.new(dir) unless dir.is_a?(Pathname)
22
+ results = []
23
+
24
+ dir.each_child(true) do |child|
25
+ next if ignored?(child.to_s)
26
+
27
+ if child.symlink?
28
+ results << child
29
+ elsif child.directory? && child.basename.to_s !~ %r{^(\.git|\.?bundle)$}
30
+ results.concat(check(child))
31
+ end
32
+ end
33
+
34
+ results
29
35
  end
30
- end
31
-
32
- results
33
- end
34
36
 
35
- def ignored?(path)
36
- path = "#{path}/" if File.directory?(path)
37
+ def ignored?(path)
38
+ path = "#{path}/" if File.directory?(path)
37
39
 
38
- !ignore_pathspec.match_paths([path], Dir.pwd).empty?
39
- end
40
+ !ignore_pathspec.match_paths([path], Dir.pwd).empty?
41
+ end
40
42
 
41
- def ignore_pathspec
42
- @ignore_pathspec ||= PathSpec.new(DEFAULT_IGNORED).tap do |pathspec|
43
- IGNORE_LIST_FILES.each do |f|
44
- next unless File.file?(f) && File.readable?(f)
43
+ def ignore_pathspec
44
+ @ignore_pathspec ||= PathSpec.new(DEFAULT_IGNORED).tap do |pathspec|
45
+ IGNORE_LIST_FILES.each do |f|
46
+ next unless File.file?(f) && File.readable?(f)
45
47
 
46
- File.open(f, 'r') { |fd| pathspec.add(fd) }
48
+ File.open(f, 'r') { |fd| pathspec.add(fd) }
49
+ end
50
+ end
47
51
  end
48
52
  end
49
53
  end
@@ -4,406 +4,409 @@ require 'yaml'
4
4
  require 'open3'
5
5
  require 'json'
6
6
 
7
- # Top level namespace for spec helper tasks.
8
- module PuppetlabsSpecHelper::Tasks end
9
-
10
- # Helpers for workfing with fixtures.
11
- module PuppetlabsSpecHelper::Tasks::FixtureHelpers
12
- # This is a helper for the self-symlink entry of fixtures.yml
13
- def source_dir
14
- Dir.pwd
15
- end
16
-
17
- # @return [String] - the name of current module
18
- def module_name
19
- raise ArgumentError unless File.file?('metadata.json') && File.readable?('metadata.json')
20
-
21
- metadata = JSON.parse(File.read('metadata.json'))
22
- metadata_name = metadata.fetch('name', nil) || ''
23
-
24
- raise ArgumentError if metadata_name.empty?
7
+ module PuppetlabsSpecHelper
8
+ module Tasks
9
+ # Helpers for working with fixtures.
10
+ module FixtureHelpers
11
+ # This is a helper for the self-symlink entry of fixtures.yml
12
+ def source_dir
13
+ Dir.pwd
14
+ end
25
15
 
26
- metadata_name.split('-').last
27
- rescue JSON::ParserError, ArgumentError
28
- File.basename(Dir.pwd).split('-').last
29
- end
16
+ # @return [String] - the name of current module
17
+ def module_name
18
+ raise ArgumentError unless File.file?('metadata.json') && File.readable?('metadata.json')
30
19
 
31
- def module_version(path)
32
- metadata_path = File.join(path, 'metadata.json')
33
- raise ArgumentError unless File.file?(metadata_path) && File.readable?(metadata_path)
20
+ metadata = JSON.parse(File.read('metadata.json'))
21
+ metadata_name = metadata.fetch('name', nil) || ''
34
22
 
35
- metadata = JSON.parse(File.read(metadata_path))
36
- metadata.fetch('version', nil) || '0.0.1'
37
- rescue JSON::ParserError, ArgumentError
38
- logger.warn "Failed to find module version at path #{path}"
39
- '0.0.1'
40
- end
23
+ raise ArgumentError if metadata_name.empty?
41
24
 
42
- # @return [Hash] - returns a hash of all the fixture repositories
43
- # @example
44
- # {"puppetlabs-stdlib"=>{"target"=>"https://gitlab.com/puppetlabs/puppet-stdlib.git",
45
- # "ref"=>nil, "branch"=>"main", "scm"=>nil,
46
- # }}
47
- def repositories
48
- @repositories ||= fixtures('repositories') || {}
49
- end
25
+ metadata_name.split('-').last
26
+ rescue JSON::ParserError, ArgumentError
27
+ File.basename(Dir.pwd).split('-').last
28
+ end
50
29
 
51
- # @return [Hash] - returns a hash of all the fixture forge modules
52
- # @example
53
- # {"puppetlabs-stdlib"=>{"target"=>"spec/fixtures/modules/stdlib",
54
- # "ref"=>nil, "branch"=>nil, "scm"=>nil,
55
- # "flags"=>"--module_repository=https://myforge.example.com/", "subdir"=>nil}}
56
- def forge_modules
57
- @forge_modules ||= fixtures('forge_modules') || {}
58
- end
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)
59
33
 
60
- # @return [Hash] - a hash of symlinks specified in the fixtures file
61
- def symlinks
62
- @symlinks ||= fixtures('symlinks') || {}
63
- end
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
64
40
 
65
- # @return [Hash] - returns a hash with the module name and the source directory
66
- def auto_symlink
67
- { module_name => "\#{source_dir}" }
68
- end
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
+ # }}
46
+ def repositories
47
+ @repositories ||= fixtures('repositories') || {}
48
+ end
69
49
 
70
- # @return [Boolean] - true if the os is a windows system
71
- def windows?
72
- !!File::ALT_SEPARATOR
73
- end
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
74
58
 
75
- def fixtures(category)
76
- fixtures_yaml = if ENV['FIXTURES_YML']
77
- ENV['FIXTURES_YML']
78
- elsif File.exist?('.fixtures.yml')
79
- '.fixtures.yml'
80
- elsif File.exist?('.fixtures.yaml')
81
- '.fixtures.yaml'
82
- else
83
- false
84
- end
59
+ # @return [Hash] - a hash of symlinks specified in the fixtures file
60
+ def symlinks
61
+ @symlinks ||= fixtures('symlinks') || {}
62
+ end
85
63
 
86
- begin
87
- fixtures = if fixtures_yaml
88
- YAML.load_file(fixtures_yaml) || { 'fixtures' => {} }
89
- else
90
- { 'fixtures' => {} }
91
- end
92
- rescue Errno::ENOENT
93
- raise("Fixtures file not found: '#{fixtures_yaml}'")
94
- rescue Psych::SyntaxError => e
95
- raise("Found malformed YAML in '#{fixtures_yaml}' on line #{e.line} column #{e.column}: #{e.problem}")
96
- end
64
+ # @return [Hash] - returns a hash with the module name and the source directory
65
+ def auto_symlink
66
+ { module_name => "\#{source_dir}" }
67
+ end
97
68
 
98
- unless fixtures.include?('fixtures')
99
- # File is non-empty, but does not specify fixtures
100
- raise("No 'fixtures' entries found in '#{fixtures_yaml}'; required")
101
- end
69
+ # @return [Boolean] - true if the os is a windows system
70
+ def windows?
71
+ !!File::ALT_SEPARATOR
72
+ end
102
73
 
103
- fixture_defaults = if fixtures.include? 'defaults'
104
- fixtures['defaults']
105
- else
106
- {}
107
- end
74
+ def fixtures(category)
75
+ fixtures_yaml = if ENV['FIXTURES_YML']
76
+ ENV['FIXTURES_YML']
77
+ elsif File.exist?('.fixtures.yml')
78
+ '.fixtures.yml'
79
+ elsif File.exist?('.fixtures.yaml')
80
+ '.fixtures.yaml'
81
+ else
82
+ false
83
+ end
84
+
85
+ begin
86
+ fixtures = if fixtures_yaml
87
+ YAML.load_file(fixtures_yaml) || { 'fixtures' => {} }
88
+ else
89
+ { 'fixtures' => {} }
90
+ end
91
+ rescue Errno::ENOENT
92
+ raise("Fixtures file not found: '#{fixtures_yaml}'")
93
+ rescue Psych::SyntaxError => e
94
+ raise("Found malformed YAML in '#{fixtures_yaml}' on line #{e.line} column #{e.column}: #{e.problem}")
95
+ end
108
96
 
109
- fixtures = fixtures['fixtures']
97
+ unless fixtures.include?('fixtures')
98
+ # File is non-empty, but does not specify fixtures
99
+ raise("No 'fixtures' entries found in '#{fixtures_yaml}'; required")
100
+ end
110
101
 
111
- if fixtures['symlinks'].nil?
112
- fixtures['symlinks'] = auto_symlink
113
- end
102
+ fixture_defaults = if fixtures.include? 'defaults'
103
+ fixtures['defaults']
104
+ else
105
+ {}
106
+ end
114
107
 
115
- result = {}
116
- if fixtures.include?(category) && !fixtures[category].nil?
117
- defaults = { 'target' => 'spec/fixtures/modules' }
108
+ fixtures = fixtures['fixtures']
118
109
 
119
- # load defaults from the `.fixtures.yml` `defaults` section
120
- # for the requested category and merge them into my defaults
121
- if fixture_defaults.include? category
122
- defaults = defaults.merge(fixture_defaults[category])
123
- end
110
+ if fixtures['symlinks'].nil?
111
+ fixtures['symlinks'] = auto_symlink
112
+ end
124
113
 
125
- fixtures[category].each do |fixture, opts|
126
- # convert a simple string fixture to a hash, by
127
- # using the string fixture as the `repo` option of the hash.
128
- if opts.instance_of?(String)
129
- opts = { 'repo' => opts }
114
+ result = {}
115
+ if fixtures.include?(category) && !fixtures[category].nil?
116
+ defaults = { 'target' => 'spec/fixtures/modules' }
117
+
118
+ # load defaults from the `.fixtures.yml` `defaults` section
119
+ # for the requested category and merge them into my defaults
120
+ if fixture_defaults.include? category
121
+ defaults = defaults.merge(fixture_defaults[category])
122
+ end
123
+
124
+ fixtures[category].each do |fixture, opts|
125
+ # convert a simple string fixture to a hash, by
126
+ # using the string fixture as the `repo` option of the hash.
127
+ if opts.instance_of?(String)
128
+ opts = { 'repo' => opts }
129
+ end
130
+ # there should be a warning or something if it's not a hash...
131
+ next unless opts.instance_of?(Hash)
132
+
133
+ # merge our options into the defaults to get the
134
+ # final option list
135
+ opts = defaults.merge(opts)
136
+
137
+ next unless include_repo?(opts['puppet_version'])
138
+
139
+ # rubocop:disable Security/Eval
140
+ # TODO: Remove eval
141
+ real_target = eval("\"#{opts['target']}\"", binding, __FILE__, __LINE__) # evaluating target reference in this context (see auto_symlink)
142
+ real_source = eval("\"#{opts['repo']}\"", binding, __FILE__, __LINE__) # evaluating repo reference in this context (see auto_symlink)
143
+
144
+ result[real_source] = validate_fixture_hash!(
145
+ 'target' => File.join(real_target, fixture),
146
+ 'ref' => opts['ref'] || opts['tag'],
147
+ 'branch' => opts['branch'],
148
+ 'scm' => opts['scm'],
149
+ 'flags' => opts['flags'],
150
+ 'subdir' => opts['subdir'],
151
+ )
152
+ end
130
153
  end
131
- # there should be a warning or something if it's not a hash...
132
- next unless opts.instance_of?(Hash)
133
-
134
- # merge our options into the defaults to get the
135
- # final option list
136
- opts = defaults.merge(opts)
137
-
138
- next unless include_repo?(opts['puppet_version'])
139
-
140
- # rubocop:disable Security/Eval
141
- # TODO: Remove eval
142
- real_target = eval("\"#{opts['target']}\"", binding, __FILE__, __LINE__) # evaluating target reference in this context (see auto_symlink)
143
- real_source = eval("\"#{opts['repo']}\"", binding, __FILE__, __LINE__) # evaluating repo reference in this context (see auto_symlink)
144
-
145
- result[real_source] = validate_fixture_hash!(
146
- 'target' => File.join(real_target, fixture),
147
- 'ref' => opts['ref'] || opts['tag'],
148
- 'branch' => opts['branch'],
149
- 'scm' => opts['scm'],
150
- 'flags' => opts['flags'],
151
- 'subdir' => opts['subdir'],
152
- )
154
+ result
153
155
  end
154
- end
155
- result
156
- end
157
156
 
158
- def validate_fixture_hash!(hash)
159
- # Can only validate git based scm
160
- return hash unless hash['scm'] == 'git'
157
+ def validate_fixture_hash!(hash)
158
+ # Can only validate git based scm
159
+ return hash unless hash['scm'] == 'git'
161
160
 
162
- # Forward slashes in the ref aren't allowed. And is probably a branch name.
163
- 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'].include?('/')
161
+ # Forward slashes in the ref aren't allowed. And is probably a branch name.
162
+ 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'].include?('/')
164
163
 
165
- hash
166
- end
164
+ hash
165
+ end
167
166
 
168
- def include_repo?(version_range)
169
- if version_range && defined?(SemanticPuppet)
170
- puppet_spec = Gem::Specification.find_by_name('puppet')
171
- puppet_version = SemanticPuppet::Version.parse(puppet_spec.version.to_s)
167
+ def include_repo?(version_range)
168
+ if version_range && defined?(SemanticPuppet)
169
+ puppet_spec = Gem::Specification.find_by_name('puppet')
170
+ puppet_version = SemanticPuppet::Version.parse(puppet_spec.version.to_s)
172
171
 
173
- constraint = SemanticPuppet::VersionRange.parse(version_range)
174
- constraint.include?(puppet_version)
175
- else
176
- true
177
- end
178
- end
172
+ constraint = SemanticPuppet::VersionRange.parse(version_range)
173
+ constraint.include?(puppet_version)
174
+ else
175
+ true
176
+ end
177
+ end
179
178
 
180
- def clone_repo(scm, remote, target, _subdir = nil, ref = nil, branch = nil, flags = nil)
181
- args = []
182
- case scm
183
- when 'hg'
184
- args.push('clone')
185
- args.push('-b', branch) if branch
186
- args.push(flags) if flags
187
- args.push(remote, target)
188
- when 'git'
189
- args.push('clone')
190
- args.push('--depth 1') unless ref
191
- args.push('-b', branch) if branch
192
- args.push(flags) if flags
193
- args.push(remote, target)
194
- else
195
- raise "Unfortunately #{scm} is not supported yet"
196
- end
197
- result = system("#{scm} #{args.flatten.join ' '}")
198
- unless File.exist?(target)
199
- raise "Failed to clone #{scm} repository #{remote} into #{target}"
200
- end
179
+ def clone_repo(scm, remote, target, _subdir = nil, ref = nil, branch = nil, flags = nil)
180
+ args = []
181
+ case scm
182
+ when 'hg'
183
+ args.push('clone')
184
+ args.push('-b', branch) if branch
185
+ args.push(flags) if flags
186
+ args.push(remote, target)
187
+ when 'git'
188
+ args.push('clone')
189
+ args.push('--depth 1') unless ref
190
+ args.push('-b', branch) if branch
191
+ args.push(flags) if flags
192
+ args.push(remote, target)
193
+ else
194
+ raise "Unfortunately #{scm} is not supported yet"
195
+ end
196
+ result = system("#{scm} #{args.flatten.join ' '}")
197
+ unless File.exist?(target)
198
+ raise "Failed to clone #{scm} repository #{remote} into #{target}"
199
+ end
201
200
 
202
- result
203
- end
201
+ result
202
+ end
204
203
 
205
- def update_repo(scm, target)
206
- args = case scm
207
- when 'hg'
208
- ['pull']
209
- when 'git'
210
- ['fetch'].tap do |git_args|
211
- git_args << '--unshallow' if shallow_git_repo?
212
- end
213
- else
214
- raise "Unfortunately #{scm} is not supported yet"
215
- end
216
- system("#{scm} #{args.flatten.join(' ')}", chdir: target)
217
- end
204
+ def update_repo(scm, target)
205
+ args = case scm
206
+ when 'hg'
207
+ ['pull']
208
+ when 'git'
209
+ ['fetch'].tap do |git_args|
210
+ git_args << '--unshallow' if shallow_git_repo?
211
+ end
212
+ else
213
+ raise "Unfortunately #{scm} is not supported yet"
214
+ end
215
+ system("#{scm} #{args.flatten.join(' ')}", chdir: target)
216
+ end
218
217
 
219
- def shallow_git_repo?
220
- File.file?(File.join('.git', 'shallow'))
221
- end
218
+ def shallow_git_repo?
219
+ File.file?(File.join('.git', 'shallow'))
220
+ end
222
221
 
223
- def revision(scm, target, ref)
224
- args = []
225
- case scm
226
- when 'hg'
227
- args.push('update', '--clean', '-r', ref)
228
- when 'git'
229
- args.push('reset', '--hard', ref)
230
- else
231
- raise "Unfortunately #{scm} is not supported yet"
232
- end
233
- result = system("#{scm} #{args.flatten.join ' '}", chdir: target)
234
- raise "Invalid ref #{ref} for #{target}" unless result
235
- end
222
+ def revision(scm, target, ref)
223
+ args = []
224
+ case scm
225
+ when 'hg'
226
+ args.push('update', '--clean', '-r', ref)
227
+ when 'git'
228
+ args.push('reset', '--hard', ref)
229
+ else
230
+ raise "Unfortunately #{scm} is not supported yet"
231
+ end
232
+ result = system("#{scm} #{args.flatten.join ' '}", chdir: target)
233
+ raise "Invalid ref #{ref} for #{target}" unless result
234
+ end
236
235
 
237
- def valid_repo?(scm, target, remote)
238
- return false unless File.directory?(target)
239
- return true if scm == 'hg'
236
+ def valid_repo?(scm, target, remote)
237
+ return false unless File.directory?(target)
238
+ return true if scm == 'hg'
240
239
 
241
- return true if git_remote_url(target) == remote
240
+ return true if git_remote_url(target) == remote
242
241
 
243
- warn "Git remote for #{target} has changed, recloning repository"
244
- FileUtils.rm_rf(target)
245
- false
246
- end
242
+ warn "Git remote for #{target} has changed, recloning repository"
243
+ FileUtils.rm_rf(target)
244
+ false
245
+ end
247
246
 
248
- def git_remote_url(target)
249
- output, status = Open3.capture2e('git', '--git-dir', File.join(target, '.git'), 'ls-remote', '--get-url', 'origin')
250
- status.success? ? output.strip : nil
251
- end
247
+ def git_remote_url(target)
248
+ output, status = Open3.capture2e('git', '--git-dir', File.join(target, '.git'), 'ls-remote', '--get-url', 'origin')
249
+ status.success? ? output.strip : nil
250
+ end
252
251
 
253
- def remove_subdirectory(target, subdir)
254
- return if subdir.nil?
255
- Dir.mktmpdir do |tmpdir|
256
- FileUtils.mv(Dir.glob("#{target}/#{subdir}/{.[^\.]*,*}"), tmpdir)
257
- FileUtils.rm_rf("#{target}/#{subdir}")
258
- FileUtils.mv(Dir.glob("#{tmpdir}/{.[^\.]*,*}"), target.to_s)
259
- end
260
- end
252
+ def remove_subdirectory(target, subdir)
253
+ return if subdir.nil?
254
+ Dir.mktmpdir do |tmpdir|
255
+ FileUtils.mv(Dir.glob("#{target}/#{subdir}/{.[^\.]*,*}"), tmpdir)
256
+ FileUtils.rm_rf("#{target}/#{subdir}")
257
+ FileUtils.mv(Dir.glob("#{tmpdir}/{.[^\.]*,*}"), target.to_s)
258
+ end
259
+ end
261
260
 
262
- # creates a logger so we can log events with certain levels
263
- def logger
264
- unless @logger
265
- require 'logger'
266
- level = if ENV['ENABLE_LOGGER']
267
- Logger::DEBUG
268
- else
269
- Logger::INFO
270
- end
271
- @logger = Logger.new($stderr)
272
- @logger.level = level
273
- end
274
- @logger
275
- end
261
+ # creates a logger so we can log events with certain levels
262
+ def logger
263
+ unless @logger
264
+ require 'logger'
265
+ level = if ENV['ENABLE_LOGGER']
266
+ Logger::DEBUG
267
+ else
268
+ Logger::INFO
269
+ end
270
+ @logger = Logger.new($stderr)
271
+ @logger.level = level
272
+ end
273
+ @logger
274
+ end
276
275
 
277
- def module_working_directory
278
- # The problem with the relative path is that PMT doesn't expand the path properly and so passing in a relative path here
279
- # becomes something like C:\somewhere\backslashes/spec/fixtures/work-dir on Windows, and then PMT barfs itself.
280
- # This has been reported as https://tickets.puppetlabs.com/browse/PUP-4884
281
- File.expand_path(ENV['MODULE_WORKING_DIR'] || 'spec/fixtures/work-dir')
282
- end
276
+ def module_working_directory
277
+ # The problem with the relative path is that PMT doesn't expand the path properly and so passing in a relative path here
278
+ # becomes something like C:\somewhere\backslashes/spec/fixtures/work-dir on Windows, and then PMT barfs itself.
279
+ # This has been reported as https://tickets.puppetlabs.com/browse/PUP-4884
280
+ File.expand_path(ENV['MODULE_WORKING_DIR'] || 'spec/fixtures/work-dir')
281
+ end
283
282
 
284
- # returns the current thread count that is currently active
285
- # a status of false or nil means the thread completed
286
- # so when anything else we count that as a active thread
287
- # @return [Integer] - current thread count
288
- def current_thread_count(items)
289
- active_threads = items.select do |_item, opts|
290
- if opts[:thread]
291
- opts[:thread].status
292
- else
293
- false
283
+ # returns the current thread count that is currently active
284
+ # a status of false or nil means the thread completed
285
+ # so when anything else we count that as a active thread
286
+ # @return [Integer] - current thread count
287
+ def current_thread_count(items)
288
+ active_threads = items.select do |_item, opts|
289
+ if opts[:thread]
290
+ opts[:thread].status
291
+ else
292
+ false
293
+ end
294
+ end
295
+ logger.debug "Current thread count #{active_threads.count}"
296
+ active_threads.count
294
297
  end
295
- end
296
- logger.debug "Current thread count #{active_threads.count}"
297
- active_threads.count
298
- end
299
298
 
300
- # @summary Set a limit on the amount threads used, defaults to 10
301
- # MAX_FIXTURE_THREAD_COUNT can be used to set this limit
302
- # @return [Integer] - returns the max_thread_count
303
- def max_thread_limit
304
- @max_thread_limit ||= (ENV['MAX_FIXTURE_THREAD_COUNT'] || 10).to_i
305
- end
299
+ # @summary Set a limit on the amount threads used, defaults to 10
300
+ # MAX_FIXTURE_THREAD_COUNT can be used to set this limit
301
+ # @return [Integer] - returns the max_thread_count
302
+ def max_thread_limit
303
+ @max_thread_limit ||= (ENV['MAX_FIXTURE_THREAD_COUNT'] || 10).to_i
304
+ end
306
305
 
307
- # @param items [Hash] - a hash of either repositories or forge modules
308
- # @param [Block] - the method you wish to use to download the item
309
- def download_items(items)
310
- items.each do |remote, opts|
311
- # get the current active threads that are alive
312
- count = current_thread_count(items)
313
- if count < max_thread_limit
314
- logger.debug "New Thread started for #{remote}"
315
- # start up a new thread and store it in the opts hash
316
- opts[:thread] = Thread.new do
317
- yield(remote, opts)
306
+ # @param items [Hash] - a hash of either repositories or forge modules
307
+ # @param [Block] - the method you wish to use to download the item
308
+ def download_items(items)
309
+ items.each do |remote, opts|
310
+ # get the current active threads that are alive
311
+ count = current_thread_count(items)
312
+ if count < max_thread_limit
313
+ logger.debug "New Thread started for #{remote}"
314
+ # start up a new thread and store it in the opts hash
315
+ opts[:thread] = Thread.new do
316
+ yield(remote, opts)
317
+ end
318
+ else
319
+ # the last thread started should be the longest wait
320
+ # Rubocop seems to push towards using select here.. however the implementation today relies on the result being
321
+ # an array. Select returns a hash which makes it unsuitable so we need to use find_all.last.
322
+ item, item_opts = items.find_all { |_i, o| o.key?(:thread) }.last # rubocop:disable Performance/Detect, Style/CollectionMethods
323
+ logger.debug "Waiting on #{item}"
324
+ item_opts[:thread].join # wait for the thread to finish
325
+ # now that we waited lets try again
326
+ redo
327
+ end
318
328
  end
319
- else
320
- # the last thread started should be the longest wait
321
- item, item_opts = items.reverse.find { |_i, o| o.key?(:thread) }
322
- logger.debug "Waiting on #{item}"
323
- item_opts[:thread].join # wait for the thread to finish
324
- # now that we waited lets try again
325
- redo
329
+ # wait for all the threads to finish
330
+ items.each { |_remote, opts| opts[:thread].join }
326
331
  end
327
- end
328
- # wait for all the threads to finish
329
- items.each { |_remote, opts| opts[:thread].join }
330
- end
331
332
 
332
- # @param target [String] - the target directory
333
- # @param link [String] - the name of the link you wish to create
334
- # works on windows and linux
335
- def setup_symlink(target, link)
336
- link = link['target']
337
- return if File.symlink?(link)
338
-
339
- logger.info("Creating symlink from #{link} to #{target}")
340
- if windows?
341
- target = File.join(File.dirname(link), target) unless Pathname.new(target).absolute?
342
- if Dir.respond_to?(:create_junction)
343
- Dir.create_junction(link, target)
344
- else
345
- system("call mklink /J \"#{link.tr('/', '\\')}\" \"#{target.tr('/', '\\')}\"")
333
+ # @param target [String] - the target directory
334
+ # @param link [String] - the name of the link you wish to create
335
+ # works on windows and linux
336
+ def setup_symlink(target, link)
337
+ link = link['target']
338
+ return if File.symlink?(link)
339
+
340
+ logger.info("Creating symlink from #{link} to #{target}")
341
+ if windows?
342
+ target = File.join(File.dirname(link), target) unless Pathname.new(target).absolute?
343
+ if Dir.respond_to?(:create_junction)
344
+ Dir.create_junction(link, target)
345
+ else
346
+ system("call mklink /J \"#{link.tr('/', '\\')}\" \"#{target.tr('/', '\\')}\"")
347
+ end
348
+ else
349
+ FileUtils.ln_sf(target, link)
350
+ end
346
351
  end
347
- else
348
- FileUtils.ln_sf(target, link)
349
- end
350
- end
351
352
 
352
- # @return [Boolean] - returns true if the module was downloaded successfully, false otherwise
353
- # @param [String] - the remote url or namespace/name of the module to download
354
- # @param [Hash] - list of options such as version, branch, ref
355
- def download_repository(remote, opts)
356
- scm = 'git'
357
- target = opts['target']
358
- subdir = opts['subdir']
359
- ref = opts['ref']
360
- scm = opts['scm'] if opts['scm']
361
- branch = opts['branch'] if opts['branch']
362
- flags = opts['flags']
363
- if valid_repo?(scm, target, remote)
364
- update_repo(scm, target)
365
- else
366
- clone_repo(scm, remote, target, subdir, ref, branch, flags)
367
- end
368
- revision(scm, target, ref) if ref
369
- remove_subdirectory(target, subdir) if subdir
370
- end
353
+ # @return [Boolean] - returns true if the module was downloaded successfully, false otherwise
354
+ # @param [String] - the remote url or namespace/name of the module to download
355
+ # @param [Hash] - list of options such as version, branch, ref
356
+ def download_repository(remote, opts)
357
+ scm = 'git'
358
+ target = opts['target']
359
+ subdir = opts['subdir']
360
+ ref = opts['ref']
361
+ scm = opts['scm'] if opts['scm']
362
+ branch = opts['branch'] if opts['branch']
363
+ flags = opts['flags']
364
+ if valid_repo?(scm, target, remote)
365
+ update_repo(scm, target)
366
+ else
367
+ clone_repo(scm, remote, target, subdir, ref, branch, flags)
368
+ end
369
+ revision(scm, target, ref) if ref
370
+ remove_subdirectory(target, subdir) if subdir
371
+ end
371
372
 
372
- # @return [String] - the spec/fixtures/modules directory in the module root folder
373
- def module_target_dir
374
- @module_target_dir ||= File.expand_path('spec/fixtures/modules')
375
- end
373
+ # @return [String] - the spec/fixtures/modules directory in the module root folder
374
+ def module_target_dir
375
+ @module_target_dir ||= File.expand_path('spec/fixtures/modules')
376
+ end
376
377
 
377
- # @return [Boolean] - returns true if the module was downloaded successfully, false otherwise
378
- # @param [String] - the remote url or namespace/name of the module to download
379
- # @param [Hash] - list of options such as version
380
- def download_module(remote, opts)
381
- ref = ''
382
- flags = ''
383
- if opts.instance_of?(String)
384
- target = opts
385
- elsif opts.instance_of?(Hash)
386
- target = opts['target']
387
- ref = " --version #{opts['ref']}" unless opts['ref'].nil?
388
- flags = " #{opts['flags']}" if opts['flags']
389
- end
378
+ # @return [Boolean] - returns true if the module was downloaded successfully, false otherwise
379
+ # @param [String] - the remote url or namespace/name of the module to download
380
+ # @param [Hash] - list of options such as version
381
+ def download_module(remote, opts)
382
+ ref = ''
383
+ flags = ''
384
+ if opts.instance_of?(String)
385
+ target = opts
386
+ elsif opts.instance_of?(Hash)
387
+ target = opts['target']
388
+ ref = " --version #{opts['ref']}" unless opts['ref'].nil?
389
+ flags = " #{opts['flags']}" if opts['flags']
390
+ end
390
391
 
391
- return false if File.directory?(target) && (ref.empty? || opts['ref'] == module_version(target))
392
+ return false if File.directory?(target) && (ref.empty? || opts['ref'] == module_version(target))
392
393
 
393
- # The PMT cannot handle multi threaded runs due to cache directory collisons
394
- # so we randomize the directory instead.
395
- # Does working_dir even need to be passed?
396
- Dir.mktmpdir do |working_dir|
397
- command = "puppet module install#{ref}#{flags} --ignore-dependencies" \
398
- ' --force' \
399
- " --module_working_dir \"#{working_dir}\"" \
400
- " --target-dir \"#{module_target_dir}\" \"#{remote}\""
394
+ # The PMT cannot handle multi threaded runs due to cache directory collisons
395
+ # so we randomize the directory instead.
396
+ # Does working_dir even need to be passed?
397
+ Dir.mktmpdir do |working_dir|
398
+ command = "puppet module install#{ref}#{flags} --ignore-dependencies" \
399
+ ' --force' \
400
+ " --module_working_dir \"#{working_dir}\"" \
401
+ " --target-dir \"#{module_target_dir}\" \"#{remote}\""
401
402
 
402
- unless system(command)
403
- raise "Failed to install module #{remote} to #{module_target_dir}"
403
+ unless system(command)
404
+ raise "Failed to install module #{remote} to #{module_target_dir}"
405
+ end
406
+ end
407
+ $CHILD_STATUS.success?
404
408
  end
405
409
  end
406
- $CHILD_STATUS.success?
407
410
  end
408
411
  end
409
412
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PuppetlabsSpecHelper
4
- VERSION = '5.0.1'
4
+ VERSION = '5.0.3'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puppetlabs_spec_helper
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.1
4
+ version: 5.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet, Inc.
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2023-01-23 00:00:00.000000000 Z
12
+ date: 2023-01-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: mocha
@@ -29,30 +29,42 @@ dependencies:
29
29
  name: pathspec
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
- - - "~>"
32
+ - - ">="
33
33
  - !ruby/object:Gem::Version
34
34
  version: '0.2'
35
+ - - "<"
36
+ - !ruby/object:Gem::Version
37
+ version: 2.0.0
35
38
  type: :runtime
36
39
  prerelease: false
37
40
  version_requirements: !ruby/object:Gem::Requirement
38
41
  requirements:
39
- - - "~>"
42
+ - - ">="
40
43
  - !ruby/object:Gem::Version
41
44
  version: '0.2'
45
+ - - "<"
46
+ - !ruby/object:Gem::Version
47
+ version: 2.0.0
42
48
  - !ruby/object:Gem::Dependency
43
49
  name: puppet-lint
44
50
  requirement: !ruby/object:Gem::Requirement
45
51
  requirements:
46
- - - "~>"
52
+ - - ">="
47
53
  - !ruby/object:Gem::Version
48
54
  version: 2.5.2
55
+ - - "<"
56
+ - !ruby/object:Gem::Version
57
+ version: 4.0.0
49
58
  type: :runtime
50
59
  prerelease: false
51
60
  version_requirements: !ruby/object:Gem::Requirement
52
61
  requirements:
53
- - - "~>"
62
+ - - ">="
54
63
  - !ruby/object:Gem::Version
55
64
  version: 2.5.2
65
+ - - "<"
66
+ - !ruby/object:Gem::Version
67
+ version: 4.0.0
56
68
  - !ruby/object:Gem::Dependency
57
69
  name: puppet-syntax
58
70
  requirement: !ruby/object:Gem::Requirement