bolt 2.29.0 → 2.33.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of bolt might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Puppetfile +15 -14
- data/bolt-modules/boltlib/lib/puppet/functions/download_file.rb +1 -1
- data/bolt-modules/boltlib/lib/puppet/functions/facts.rb +6 -0
- data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_query.rb +2 -2
- data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +1 -1
- data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +1 -1
- data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +1 -1
- data/bolt-modules/boltlib/lib/puppet/functions/run_task_with.rb +1 -1
- data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +1 -1
- data/bolt-modules/boltlib/lib/puppet/functions/write_file.rb +2 -2
- data/bolt-modules/out/lib/puppet/functions/out/message.rb +44 -1
- data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +3 -0
- data/guides/logging.txt +18 -0
- data/guides/module.txt +19 -0
- data/guides/modulepath.txt +25 -0
- data/lib/bolt/bolt_option_parser.rb +48 -9
- data/lib/bolt/catalog.rb +1 -1
- data/lib/bolt/cli.rb +154 -116
- data/lib/bolt/config.rb +13 -1
- data/lib/bolt/config/modulepath.rb +30 -0
- data/lib/bolt/config/options.rb +32 -13
- data/lib/bolt/config/transport/options.rb +2 -2
- data/lib/bolt/error.rb +4 -0
- data/lib/bolt/executor.rb +13 -13
- data/lib/bolt/inventory.rb +10 -9
- data/lib/bolt/module_installer.rb +198 -0
- data/lib/bolt/{puppetfile → module_installer}/installer.rb +3 -2
- data/lib/bolt/module_installer/puppetfile.rb +117 -0
- data/lib/bolt/module_installer/puppetfile/forge_module.rb +54 -0
- data/lib/bolt/module_installer/puppetfile/git_module.rb +37 -0
- data/lib/bolt/module_installer/puppetfile/module.rb +26 -0
- data/lib/bolt/module_installer/resolver.rb +76 -0
- data/lib/bolt/module_installer/specs.rb +93 -0
- data/lib/bolt/module_installer/specs/forge_spec.rb +85 -0
- data/lib/bolt/module_installer/specs/git_spec.rb +179 -0
- data/lib/bolt/outputter.rb +2 -45
- data/lib/bolt/outputter/human.rb +78 -18
- data/lib/bolt/outputter/json.rb +22 -7
- data/lib/bolt/outputter/logger.rb +2 -2
- data/lib/bolt/pal.rb +55 -45
- data/lib/bolt/pal/yaml_plan.rb +4 -2
- data/lib/bolt/pal/yaml_plan/evaluator.rb +23 -1
- data/lib/bolt/pal/yaml_plan/loader.rb +14 -9
- data/lib/bolt/plugin.rb +1 -1
- data/lib/bolt/plugin/module.rb +1 -1
- data/lib/bolt/project.rb +32 -21
- data/lib/bolt/project_migrator.rb +80 -0
- data/lib/bolt/project_migrator/base.rb +39 -0
- data/lib/bolt/project_migrator/config.rb +67 -0
- data/lib/bolt/project_migrator/inventory.rb +67 -0
- data/lib/bolt/project_migrator/modules.rb +200 -0
- data/lib/bolt/result.rb +23 -11
- data/lib/bolt/shell/bash.rb +15 -9
- data/lib/bolt/shell/powershell.rb +11 -6
- data/lib/bolt/transport/base.rb +18 -18
- data/lib/bolt/transport/docker.rb +23 -6
- data/lib/bolt/transport/orch.rb +23 -14
- data/lib/bolt/transport/remote.rb +2 -2
- data/lib/bolt/transport/simple.rb +6 -6
- data/lib/bolt/transport/ssh/connection.rb +1 -1
- data/lib/bolt/util.rb +22 -0
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/acl.rb +2 -2
- data/lib/bolt_server/base_config.rb +3 -3
- data/lib/bolt_server/schemas/partials/task.json +17 -2
- data/lib/bolt_server/transport_app.rb +92 -12
- data/lib/bolt_spec/bolt_context.rb +4 -2
- data/lib/bolt_spec/plans.rb +1 -1
- data/lib/bolt_spec/plans/action_stubs/command_stub.rb +1 -1
- data/lib/bolt_spec/plans/action_stubs/script_stub.rb +1 -1
- data/lib/bolt_spec/plans/mock_executor.rb +6 -6
- data/lib/bolt_spec/run.rb +1 -1
- metadata +29 -10
- data/lib/bolt/project_migrate.rb +0 -138
- data/lib/bolt/puppetfile.rb +0 -160
- data/lib/bolt/puppetfile/module.rb +0 -89
- data/lib/bolt_server/pe/pal.rb +0 -67
- data/modules/secure_env_vars/plans/init.pp +0 -20
@@ -1,19 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'r10k/cli'
|
4
3
|
require 'bolt/r10k_log_proxy'
|
5
4
|
require 'bolt/error'
|
6
5
|
|
7
6
|
# This class is used to install modules from a Puppetfile to a module directory.
|
8
7
|
#
|
9
8
|
module Bolt
|
10
|
-
class
|
9
|
+
class ModuleInstaller
|
11
10
|
class Installer
|
12
11
|
def initialize(config = {})
|
13
12
|
@config = config
|
14
13
|
end
|
15
14
|
|
16
15
|
def install(path, moduledir)
|
16
|
+
require 'r10k/cli'
|
17
|
+
|
17
18
|
unless File.exist?(path)
|
18
19
|
raise Bolt::FileError.new(
|
19
20
|
"Could not find a Puppetfile at #{path}",
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bolt/error'
|
4
|
+
require 'bolt/module_installer/puppetfile/forge_module'
|
5
|
+
require 'bolt/module_installer/puppetfile/git_module'
|
6
|
+
|
7
|
+
# This class manages the logical contents of a Puppetfile. It includes methods
|
8
|
+
# for parsing and generating a Puppetfile.
|
9
|
+
#
|
10
|
+
module Bolt
|
11
|
+
class ModuleInstaller
|
12
|
+
class Puppetfile
|
13
|
+
attr_reader :modules
|
14
|
+
|
15
|
+
def initialize(modules = [])
|
16
|
+
@modules = modules
|
17
|
+
end
|
18
|
+
|
19
|
+
# Loads a Puppetfile and parses its modules.
|
20
|
+
#
|
21
|
+
def self.parse(path, skip_unsupported_modules: false)
|
22
|
+
require 'puppetfile-resolver'
|
23
|
+
|
24
|
+
return new unless path.exist?
|
25
|
+
|
26
|
+
begin
|
27
|
+
parsed = ::PuppetfileResolver::Puppetfile::Parser::R10KEval.parse(File.read(path))
|
28
|
+
rescue StandardError => e
|
29
|
+
raise Bolt::Error.new(
|
30
|
+
"Unable to parse Puppetfile #{path}: #{e.message}",
|
31
|
+
'bolt/puppetfile-parsing'
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
unless parsed.valid?
|
36
|
+
raise Bolt::ValidationError, <<~MSG
|
37
|
+
Unable to parse Puppetfile #{path}:
|
38
|
+
#{parsed.validation_errors.join("\n\n")}.
|
39
|
+
This may not be a Puppetfile managed by Bolt.
|
40
|
+
MSG
|
41
|
+
end
|
42
|
+
|
43
|
+
modules = parsed.modules.each_with_object([]) do |mod, acc|
|
44
|
+
case mod.module_type
|
45
|
+
when :forge
|
46
|
+
acc << ForgeModule.new(
|
47
|
+
mod.title,
|
48
|
+
mod.version.is_a?(String) ? mod.version[1..-1] : nil
|
49
|
+
)
|
50
|
+
when :git
|
51
|
+
acc << GitModule.new(
|
52
|
+
mod.name,
|
53
|
+
mod.remote,
|
54
|
+
mod.ref || mod.commit || mod.tag
|
55
|
+
)
|
56
|
+
else
|
57
|
+
unless skip_unsupported_modules
|
58
|
+
raise Bolt::ValidationError,
|
59
|
+
"Cannot parse Puppetfile at #{path}, module '#{mod.title}' is not a "\
|
60
|
+
"Puppet Forge or Git module."
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
new(modules)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Writes a Puppetfile that includes specifications for each of the
|
69
|
+
# modules.
|
70
|
+
#
|
71
|
+
def write(path, moduledir = nil)
|
72
|
+
File.open(path, 'w') do |file|
|
73
|
+
if moduledir
|
74
|
+
file.puts "# This Puppetfile is managed by Bolt. Do not edit."
|
75
|
+
file.puts "# For more information, see https://pup.pt/bolt-modules"
|
76
|
+
file.puts
|
77
|
+
file.puts "# The following directive installs modules to the managed moduledir."
|
78
|
+
file.puts "moduledir '#{moduledir.basename}'"
|
79
|
+
file.puts
|
80
|
+
end
|
81
|
+
|
82
|
+
@modules.each { |mod| file.puts mod.to_spec }
|
83
|
+
end
|
84
|
+
rescue SystemCallError => e
|
85
|
+
raise Bolt::FileError.new(
|
86
|
+
"#{e.message}: unable to write Puppetfile.",
|
87
|
+
path
|
88
|
+
)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Asserts that the Puppetfile satisfies the given specifications.
|
92
|
+
#
|
93
|
+
def assert_satisfies(specs)
|
94
|
+
unsatisfied_specs = specs.specs.reject do |spec|
|
95
|
+
@modules.any? do |mod|
|
96
|
+
spec.satisfied_by?(mod)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
return if unsatisfied_specs.empty?
|
101
|
+
|
102
|
+
command = Bolt::Util.windows? ? 'Install-BoltModule -Force' : 'bolt module install --force'
|
103
|
+
|
104
|
+
message = <<~MESSAGE.chomp
|
105
|
+
Puppetfile does not include modules that satisfy the following specifications:
|
106
|
+
|
107
|
+
#{unsatisfied_specs.map(&:to_hash).to_yaml.lines.drop(1).join.chomp}
|
108
|
+
|
109
|
+
This may not be a Puppetfile managed by Bolt. To forcibly overwrite the
|
110
|
+
Puppetfile, run '#{command}'.
|
111
|
+
MESSAGE
|
112
|
+
|
113
|
+
raise Bolt::Error.new(message, 'bolt/missing-module-specs')
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'semantic_puppet'
|
4
|
+
require 'bolt/module_installer/puppetfile/module'
|
5
|
+
|
6
|
+
# This class represents a resolved Forge module.
|
7
|
+
#
|
8
|
+
module Bolt
|
9
|
+
class ModuleInstaller
|
10
|
+
class Puppetfile
|
11
|
+
class ForgeModule < Module
|
12
|
+
attr_reader :version
|
13
|
+
|
14
|
+
def initialize(name, version)
|
15
|
+
super(name)
|
16
|
+
@version = parse_version(version)
|
17
|
+
@type = :forge
|
18
|
+
end
|
19
|
+
|
20
|
+
# Parses the version into a Semantic Puppet version.
|
21
|
+
#
|
22
|
+
private def parse_version(version)
|
23
|
+
return unless version.is_a?(String)
|
24
|
+
|
25
|
+
unless SemanticPuppet::Version.valid?(version)
|
26
|
+
raise Bolt::ValidationError,
|
27
|
+
"Invalid version for Forge module #{@full_name}: #{version.inspect}"
|
28
|
+
end
|
29
|
+
|
30
|
+
SemanticPuppet::Version.parse(version)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns a Puppetfile module specification.
|
34
|
+
#
|
35
|
+
def to_spec
|
36
|
+
if @version
|
37
|
+
"mod '#{@full_name}', '#{@version}'"
|
38
|
+
else
|
39
|
+
"mod '#{@full_name}'"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns a hash that can be used to create a module specification.
|
44
|
+
#
|
45
|
+
def to_hash
|
46
|
+
{
|
47
|
+
'name' => @full_name,
|
48
|
+
'version_requirement' => @version ? @version.to_s : nil
|
49
|
+
}.compact
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bolt/module_installer/puppetfile/module'
|
4
|
+
|
5
|
+
# This class represents a resolved Git module.
|
6
|
+
#
|
7
|
+
module Bolt
|
8
|
+
class ModuleInstaller
|
9
|
+
class Puppetfile
|
10
|
+
class GitModule < Module
|
11
|
+
attr_reader :git, :ref
|
12
|
+
|
13
|
+
def initialize(name, git, ref)
|
14
|
+
super(name)
|
15
|
+
@git = git
|
16
|
+
@ref = ref
|
17
|
+
@type = :git
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns a Puppetfile module specification.
|
21
|
+
#
|
22
|
+
def to_spec
|
23
|
+
"mod '#{@name}',\n git: '#{@git}',\n ref: '#{@ref}'"
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns a hash that can be used to create a module specification.
|
27
|
+
#
|
28
|
+
def to_hash
|
29
|
+
{
|
30
|
+
'git' => @git,
|
31
|
+
'ref' => @ref
|
32
|
+
}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bolt/error'
|
4
|
+
|
5
|
+
module Bolt
|
6
|
+
class ModuleInstaller
|
7
|
+
class Puppetfile
|
8
|
+
class Module
|
9
|
+
attr_reader :full_name, :name, :type
|
10
|
+
|
11
|
+
def initialize(name)
|
12
|
+
@full_name, @name = parse_name(name)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Formats the full name and extracts the module name.
|
16
|
+
#
|
17
|
+
protected def parse_name(name)
|
18
|
+
full_name = name.tr('-', '/')
|
19
|
+
first, second = full_name.split('/', 2)
|
20
|
+
|
21
|
+
[full_name, second || first]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bolt/error'
|
4
|
+
require 'bolt/module_installer/puppetfile'
|
5
|
+
require 'bolt/module_installer/specs'
|
6
|
+
|
7
|
+
module Bolt
|
8
|
+
class ModuleInstaller
|
9
|
+
class Resolver
|
10
|
+
# Resolves module specs and returns a Puppetfile object.
|
11
|
+
#
|
12
|
+
def resolve(specs)
|
13
|
+
require 'puppetfile-resolver'
|
14
|
+
|
15
|
+
# Build the document model from the specs.
|
16
|
+
document = PuppetfileResolver::Puppetfile::Document.new('')
|
17
|
+
|
18
|
+
specs.specs.each do |spec|
|
19
|
+
document.add_module(spec.to_resolver_module)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Make sure the document model is valid.
|
23
|
+
unless document.valid?
|
24
|
+
message = <<~MESSAGE.chomp
|
25
|
+
Unable to resolve module specifications:
|
26
|
+
|
27
|
+
#{document.validation_errors.map(&:message).join("\n")}
|
28
|
+
MESSAGE
|
29
|
+
|
30
|
+
raise Bolt::Error.new(message, 'bolt/module-resolver-error')
|
31
|
+
end
|
32
|
+
|
33
|
+
# Create the resolver using the Puppetfile model. nil disables Puppet
|
34
|
+
# version restrictions.
|
35
|
+
resolver = PuppetfileResolver::Resolver.new(document, nil)
|
36
|
+
|
37
|
+
# Configure and resolve the dependency graph, catching any errors
|
38
|
+
# raised by puppetfile-resolver and re-raising them as Bolt errors.
|
39
|
+
begin
|
40
|
+
result = resolver.resolve(
|
41
|
+
cache: nil,
|
42
|
+
ui: nil,
|
43
|
+
module_paths: [],
|
44
|
+
allow_missing_modules: false
|
45
|
+
)
|
46
|
+
rescue StandardError => e
|
47
|
+
raise Bolt::Error.new(e.message, 'bolt/module-resolver-error')
|
48
|
+
end
|
49
|
+
|
50
|
+
# Convert the specs returned from the resolver into Bolt module objects.
|
51
|
+
modules = result.specifications.values.each_with_object([]) do |mod, acc|
|
52
|
+
# Skip over anything that isn't a module spec, such as a Puppet spec.
|
53
|
+
next unless mod.is_a? PuppetfileResolver::Models::ModuleSpecification
|
54
|
+
|
55
|
+
case mod.origin
|
56
|
+
when :forge
|
57
|
+
acc << Bolt::ModuleInstaller::Puppetfile::ForgeModule.new(
|
58
|
+
"#{mod.owner}/#{mod.name}",
|
59
|
+
mod.version.to_s
|
60
|
+
)
|
61
|
+
when :git
|
62
|
+
spec = specs.specs.find { |s| s.name == mod.name }
|
63
|
+
acc << Bolt::ModuleInstaller::Puppetfile::GitModule.new(
|
64
|
+
spec.name,
|
65
|
+
spec.git,
|
66
|
+
spec.sha
|
67
|
+
)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Create the Puppetfile object.
|
72
|
+
Bolt::ModuleInstaller::Puppetfile.new(modules)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bolt/error'
|
4
|
+
require 'bolt/module_installer/specs/forge_spec'
|
5
|
+
require 'bolt/module_installer/specs/git_spec'
|
6
|
+
|
7
|
+
module Bolt
|
8
|
+
class ModuleInstaller
|
9
|
+
class Specs
|
10
|
+
def initialize(specs = [])
|
11
|
+
@specs = []
|
12
|
+
add_specs(specs)
|
13
|
+
assert_unique_names
|
14
|
+
end
|
15
|
+
|
16
|
+
# Creates a list of specs from the modules in a Puppetfile object.
|
17
|
+
#
|
18
|
+
def self.from_puppetfile(puppetfile)
|
19
|
+
new(puppetfile.modules.map(&:to_hash))
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns a list of specs.
|
23
|
+
#
|
24
|
+
def specs
|
25
|
+
@specs.uniq(&:name)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns true if the specs includes the given name.
|
29
|
+
#
|
30
|
+
def include?(name)
|
31
|
+
_owner, name = name.tr('-', '/').split('/', 2)
|
32
|
+
@specs.any? { |spec| spec.name == name }
|
33
|
+
end
|
34
|
+
|
35
|
+
# Adds a spec.
|
36
|
+
#
|
37
|
+
def add_specs(*specs)
|
38
|
+
specs.flatten.map do |spec|
|
39
|
+
case spec
|
40
|
+
when Hash
|
41
|
+
@specs.unshift spec_from_hash(spec)
|
42
|
+
else
|
43
|
+
@specs.unshift spec
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Parses a spec hash into a spec object.
|
49
|
+
#
|
50
|
+
private def spec_from_hash(hash)
|
51
|
+
return ForgeSpec.new(hash) if ForgeSpec.implements?(hash)
|
52
|
+
return GitSpec.new(hash) if GitSpec.implements?(hash)
|
53
|
+
|
54
|
+
raise Bolt::ValidationError, <<~MESSAGE.chomp
|
55
|
+
Invalid module specification:
|
56
|
+
#{hash.to_yaml.lines.drop(1).join.chomp}
|
57
|
+
|
58
|
+
To read more about specifying modules, see https://pup.pt/bolt-modules
|
59
|
+
MESSAGE
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns true if all specs are satisfied by the modules in a Puppetfile.
|
63
|
+
#
|
64
|
+
def satisfied_by?(puppetfile)
|
65
|
+
@specs.all? do |spec|
|
66
|
+
puppetfile.modules.any? do |mod|
|
67
|
+
spec.satisfied_by?(mod)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Asserts that all specs are unique by name. The puppetfile-resolver
|
73
|
+
# library also does this, but the error it raises isn't as helpful.
|
74
|
+
#
|
75
|
+
private def assert_unique_names
|
76
|
+
duplicates = @specs.group_by(&:name).select { |_name, specs| specs.count > 1 }
|
77
|
+
|
78
|
+
if duplicates.any?
|
79
|
+
message = String.new
|
80
|
+
|
81
|
+
duplicates.each do |name, duplicate_specs|
|
82
|
+
message << <<~MESSAGE
|
83
|
+
Detected multiple module specifications with name #{name}:
|
84
|
+
#{duplicate_specs.map(&:to_hash).to_yaml.lines.drop(1).join}
|
85
|
+
MESSAGE
|
86
|
+
end
|
87
|
+
|
88
|
+
raise Bolt::Error.new(message.chomp, "bolt/duplicate-spec-name-error")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'semantic_puppet'
|
4
|
+
require 'set'
|
5
|
+
|
6
|
+
require 'bolt/error'
|
7
|
+
|
8
|
+
# This class represents a Forge module specification.
|
9
|
+
#
|
10
|
+
module Bolt
|
11
|
+
class ModuleInstaller
|
12
|
+
class Specs
|
13
|
+
class ForgeSpec
|
14
|
+
NAME_REGEX = %r{\A[a-zA-Z0-9]+[-/](?<name>[a-z][a-z0-9_]*)\z}.freeze
|
15
|
+
REQUIRED_KEYS = Set.new(%w[name]).freeze
|
16
|
+
KNOWN_KEYS = Set.new(%w[name version_requirement]).freeze
|
17
|
+
|
18
|
+
attr_reader :full_name, :name, :semantic_version, :type
|
19
|
+
|
20
|
+
def initialize(init_hash)
|
21
|
+
@full_name, @name = parse_name(init_hash['name'])
|
22
|
+
@version_requirement, @semantic_version = parse_version_requirement(init_hash['version_requirement'])
|
23
|
+
@type = :forge
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.implements?(hash)
|
27
|
+
KNOWN_KEYS.superset?(hash.keys.to_set) && REQUIRED_KEYS.subset?(hash.keys.to_set)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Formats the full name and extracts the module name.
|
31
|
+
#
|
32
|
+
private def parse_name(name)
|
33
|
+
unless (match = name.match(NAME_REGEX))
|
34
|
+
raise Bolt::ValidationError,
|
35
|
+
"Invalid name for Forge module specification: #{name}. Name must match "\
|
36
|
+
"'owner/name'. Owner segment may only include letters or digits. Name "\
|
37
|
+
"segment must start with a lowercase letter and may only include lowercase "\
|
38
|
+
"letters, digits, and underscores."
|
39
|
+
end
|
40
|
+
|
41
|
+
[name.tr('-', '/'), match[:name]]
|
42
|
+
end
|
43
|
+
|
44
|
+
# Parses the version into a Semantic Puppet version range.
|
45
|
+
#
|
46
|
+
private def parse_version_requirement(version_requirement)
|
47
|
+
[version_requirement, SemanticPuppet::VersionRange.parse(version_requirement || '>= 0')]
|
48
|
+
rescue StandardError
|
49
|
+
raise Bolt::ValidationError,
|
50
|
+
"Invalid version requirement for Forge module specification #{@full_name}: "\
|
51
|
+
"#{version_requirement.inspect}"
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns true if the specification is satisfied by the module.
|
55
|
+
#
|
56
|
+
def satisfied_by?(mod)
|
57
|
+
@type == mod.type &&
|
58
|
+
@full_name.downcase == mod.full_name.downcase &&
|
59
|
+
!mod.version.nil? &&
|
60
|
+
@semantic_version.cover?(mod.version)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns a hash matching the module spec in bolt-project.yaml
|
64
|
+
#
|
65
|
+
def to_hash
|
66
|
+
{
|
67
|
+
'name' => @full_name,
|
68
|
+
'version_requirement' => @version_requirement
|
69
|
+
}.compact
|
70
|
+
end
|
71
|
+
|
72
|
+
# Creates a PuppetfileResolver::Puppetfile::ForgeModule object, which is
|
73
|
+
# used to generate a graph of resolved modules.
|
74
|
+
#
|
75
|
+
def to_resolver_module
|
76
|
+
require 'puppetfile-resolver'
|
77
|
+
|
78
|
+
PuppetfileResolver::Puppetfile::ForgeModule.new(@full_name).tap do |mod|
|
79
|
+
mod.version = @version_requirement
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|