bolt 2.31.0 → 2.32.0

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.

@@ -1,149 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bolt/error'
4
- require 'bolt/puppetfile/module'
5
-
6
- # This class manages the logical contents of a Puppetfile. It includes methods
7
- # for parsing a Puppetfile and its modules, resolving module dependencies,
8
- # and writing a Puppetfile.
9
- #
10
- module Bolt
11
- class Puppetfile
12
- attr_reader :modules
13
-
14
- def initialize(modules = [])
15
- @modules = Set.new
16
- add_modules(modules)
17
- end
18
-
19
- # Loads a Puppetfile and parses its module specifications, returning a
20
- # Bolt::Puppetfile object with the modules set.
21
- #
22
- def self.parse(path, skip_unsupported_modules: false)
23
- require 'puppetfile-resolver'
24
- require 'puppetfile-resolver/puppetfile/parser/r10k_eval'
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
- # valid? Just checks if validation_errors is empty, so if we get here we know it's not.
37
- raise Bolt::ValidationError, <<~MSG
38
- Unable to parse Puppetfile #{path}:
39
- #{parsed.validation_errors.join("\n\n")}.
40
- This may not be a Puppetfile managed by Bolt.
41
- MSG
42
- end
43
-
44
- modules = parsed.modules.each_with_object([]) do |mod, acc|
45
- unless mod.instance_of? PuppetfileResolver::Puppetfile::ForgeModule
46
- next if skip_unsupported_modules
47
-
48
- raise Bolt::ValidationError,
49
- "Module '#{mod.title}' is not a Puppet Forge module. Unable to "\
50
- "parse Puppetfile #{path}."
51
- end
52
-
53
- acc << Bolt::Puppetfile::Module.new(mod.owner, mod.name, mod.version)
54
- end
55
-
56
- new(modules)
57
- end
58
-
59
- # Writes a Puppetfile that includes specifications for each of the
60
- # modules.
61
- #
62
- def write(path, moduledir = nil)
63
- File.open(path, 'w') do |file|
64
- if moduledir
65
- file.puts "# This Puppetfile is managed by Bolt. Do not edit."
66
- file.puts "# For more information, see https://pup.pt/bolt-modules"
67
- file.puts
68
- file.puts "# The following directive installs modules to the managed moduledir."
69
- file.puts "moduledir '#{moduledir.basename}'"
70
- file.puts
71
- end
72
-
73
- modules.each { |mod| file.puts mod.to_spec }
74
- end
75
- rescue SystemCallError => e
76
- raise Bolt::FileError.new(
77
- "#{e.message}: unable to write Puppetfile.",
78
- path
79
- )
80
- end
81
-
82
- # Resolves module dependencies using the puppetfile-resolver library. The
83
- # resolver will return a document model including all module dependencies
84
- # and the latest version that can be installed for each. The document model
85
- # is parsed and turned into a Set of Bolt::Puppetfile::Module objects.
86
- #
87
- def resolve
88
- require 'puppetfile-resolver'
89
-
90
- # Build the document model from the modules.
91
- model = PuppetfileResolver::Puppetfile::Document.new('')
92
-
93
- @modules.each do |mod|
94
- model.add_module(
95
- PuppetfileResolver::Puppetfile::ForgeModule.new(mod.title).tap do |tap|
96
- tap.version = mod.version || :latest
97
- end
98
- )
99
- end
100
-
101
- # Make sure the Puppetfile model is valid.
102
- unless model.valid?
103
- raise Bolt::ValidationError,
104
- "Unable to resolve dependencies for modules: #{@modules.map(&:title).join(', ')}"
105
- end
106
-
107
- # Create the resolver using the Puppetfile model. nil disables Puppet
108
- # version restrictions.
109
- resolver = PuppetfileResolver::Resolver.new(model, nil)
110
-
111
- # Configure and resolve the dependency graph, catching any errors
112
- # raised by puppetfile-resolver and re-raising them as Bolt errors.
113
- begin
114
- result = resolver.resolve(
115
- cache: nil,
116
- ui: nil,
117
- module_paths: [],
118
- allow_missing_modules: false
119
- )
120
- rescue StandardError => e
121
- raise Bolt::Error.new(e.message, 'bolt/puppetfile-resolver-error')
122
- end
123
-
124
- # Turn specifications into module objects. This will skip over anything that is not
125
- # a module specification (i.e. a Puppet version specification).
126
- @modules = result.specifications.each_with_object(Set.new) do |(_name, spec), acc|
127
- next unless spec.instance_of? PuppetfileResolver::Models::ModuleSpecification
128
- acc << Bolt::Puppetfile::Module.new(spec.owner, spec.name, spec.version.to_s)
129
- end
130
- end
131
-
132
- # Adds to the set of modules.
133
- #
134
- def add_modules(modules)
135
- Array(modules).each do |mod|
136
- case mod
137
- when Bolt::Puppetfile::Module
138
- @modules << mod
139
- when Hash
140
- @modules << Bolt::Puppetfile::Module.from_hash(mod)
141
- else
142
- raise Bolt::ValidationError, "Module must be a Bolt::Puppetfile::Module or Hash."
143
- end
144
- end
145
-
146
- @modules
147
- end
148
- end
149
- end
@@ -1,93 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bolt/error'
4
-
5
- # This class represents a module specification. It used by the Bolt::Puppetfile
6
- # class to have a consistent API for accessing a module's attributes.
7
- #
8
- module Bolt
9
- class Puppetfile
10
- class Module
11
- attr_reader :owner, :name, :version
12
-
13
- def initialize(owner, name, version = nil)
14
- @owner = owner
15
- @name = name
16
-
17
- if version.is_a?(String)
18
- @version = version[0] == '=' ? version[1..-1] : version
19
- end
20
- end
21
-
22
- # Creates a new module from a hash.
23
- #
24
- def self.from_hash(mod)
25
- unless mod['name'].is_a?(String)
26
- raise Bolt::ValidationError,
27
- "Module name must be a String, not #{mod['name'].inspect}"
28
- end
29
-
30
- owner, name = mod['name'].tr('/', '-').split('-', 2)
31
-
32
- unless owner && name
33
- raise Bolt::ValidationError, "Module name #{mod['name']} must include both the owner and module name."
34
- end
35
-
36
- new(owner, name, mod['version_requirement'])
37
- end
38
-
39
- # Returns the module's title.
40
- #
41
- def title
42
- "#{@owner}-#{@name}"
43
- end
44
- alias to_s title
45
-
46
- # Checks two modules for equality.
47
- #
48
- def eql?(other)
49
- self.class == other.class &&
50
- @owner == other.owner &&
51
- @name == other.name &&
52
- versions_intersect?(other)
53
- end
54
- alias == eql?
55
-
56
- # Returns true if the versions of two modules intersect. Used to determine
57
- # if an installed module satisfies the version requirement of another.
58
- #
59
- def versions_intersect?(other)
60
- range = ::SemanticPuppet::VersionRange.parse(@version || '')
61
- other_range = ::SemanticPuppet::VersionRange.parse(other.version || '')
62
-
63
- range.intersection(other_range) != ::SemanticPuppet::VersionRange::EMPTY_RANGE
64
- end
65
-
66
- # Hashes the module.
67
- #
68
- def hash
69
- [@owner, @name].hash
70
- end
71
-
72
- # Returns a hash representation similar to the module
73
- # declaration.
74
- #
75
- def to_hash
76
- {
77
- 'name' => title,
78
- 'version_requirement' => version
79
- }.compact
80
- end
81
-
82
- # Returns the Puppetfile specification for the module.
83
- #
84
- def to_spec
85
- if @version
86
- "mod #{title.inspect}, #{@version.inspect}"
87
- else
88
- "mod #{title.inspect}"
89
- end
90
- end
91
- end
92
- end
93
- end