bolt 3.23.1 → 3.26.1

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,9 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'json'
4
+ require 'net/http'
5
+ require 'open3'
4
6
  require 'set'
5
7
 
6
8
  require_relative '../../../bolt/error'
9
+ require_relative '../../../bolt/logger'
10
+ require_relative '../../../bolt/module_installer/specs/id/gitclone'
11
+ require_relative '../../../bolt/module_installer/specs/id/github'
12
+ require_relative '../../../bolt/module_installer/specs/id/gitlab'
7
13
 
8
14
  # This class represents a Git module specification.
9
15
  #
@@ -17,12 +23,19 @@ module Bolt
17
23
 
18
24
  attr_reader :git, :ref, :resolve, :type
19
25
 
20
- def initialize(init_hash)
21
- @resolve = init_hash.key?('resolve') ? init_hash['resolve'] : true
22
- @name = parse_name(init_hash['name'])
23
- @git, @repo = parse_git(init_hash['git'])
24
- @ref = init_hash['ref']
25
- @type = :git
26
+ def initialize(init_hash, config = {})
27
+ @logger = Bolt::Logger.logger(self)
28
+ @resolve = init_hash.key?('resolve') ? init_hash['resolve'] : true
29
+ @git = init_hash['git']
30
+ @ref = init_hash['ref']
31
+ @name = parse_name(init_hash['name'])
32
+ @proxy = config.dig('proxy')
33
+ @type = :git
34
+
35
+ unless @resolve == true || @resolve == false
36
+ raise Bolt::ValidationError,
37
+ "Option 'resolve' for module spec #{@git} must be a Boolean"
38
+ end
26
39
 
27
40
  if @name.nil? && @resolve == false
28
41
  raise Bolt::ValidationError,
@@ -30,9 +43,9 @@ module Bolt
30
43
  "must include a 'name' key when 'resolve' is false."
31
44
  end
32
45
 
33
- unless @resolve == true || @resolve == false
46
+ unless valid_url?(@git)
34
47
  raise Bolt::ValidationError,
35
- "Option 'resolve' for module spec #{@git} must be a Boolean"
48
+ "Invalid URI #{@git}. Valid URIs must begin with 'git@', 'http://', or 'https://'."
36
49
  end
37
50
  end
38
51
 
@@ -57,22 +70,6 @@ module Bolt
57
70
  match[:name]
58
71
  end
59
72
 
60
- # Gets the repo from the git URL.
61
- #
62
- private def parse_git(git)
63
- return [git, nil] unless @resolve
64
-
65
- repo = if git.start_with?('git@github.com:')
66
- git.split('git@github.com:').last.split('.git').first
67
- elsif git.start_with?('https://github.com')
68
- git.split('https://github.com/').last.split('.git').first
69
- else
70
- raise Bolt::ValidationError, invalid_git_msg(git)
71
- end
72
-
73
- [git, repo]
74
- end
75
-
76
73
  # Returns true if the specification is satisfied by the module.
77
74
  #
78
75
  def satisfied_by?(mod)
@@ -88,14 +85,6 @@ module Bolt
88
85
  }
89
86
  end
90
87
 
91
- # Returns an error message that the provided repo is not a git repo or
92
- # is private.
93
- #
94
- private def invalid_git_msg(repo_name)
95
- "#{repo_name} is not a public GitHub repository. See https://pup.pt/no-resolve "\
96
- "for information on how to install this module."
97
- end
98
-
99
88
  # Returns a PuppetfileResolver::Model::GitModule object for resolving.
100
89
  #
101
90
  def to_resolver_module
@@ -107,90 +96,53 @@ module Bolt
107
96
  end
108
97
  end
109
98
 
110
- # Resolves the module's title from the module metadata. This is lazily
111
- # resolved since Bolt does not always need to know a Git module's name.
99
+ # Returns the module's name.
112
100
  #
113
101
  def name
114
- @name ||= begin
115
- url = "https://raw.githubusercontent.com/#{@repo}/#{sha}/metadata.json"
116
- response = make_request(:Get, url)
117
-
118
- case response
119
- when Net::HTTPOK
120
- body = JSON.parse(response.body)
121
-
122
- unless body.key?('name')
123
- raise Bolt::Error.new(
124
- "Missing name in metadata.json at #{git}. This is not a valid module.",
125
- "bolt/missing-module-name-error"
126
- )
127
- end
128
-
129
- parse_name(body['name'])
130
- else
131
- raise Bolt::Error.new(
132
- "Missing metadata.json at #{git}. This is not a valid module.",
133
- "bolt/missing-module-metadata-error"
134
- )
135
- end
136
- end
102
+ @name ||= parse_name(id.name)
137
103
  end
138
104
 
139
- # Resolves the SHA for the specified ref. This is lazily resolved since
140
- # Bolt does not always need to know a Git module's SHA.
105
+ # Returns the SHA for the module's ref.
141
106
  #
142
107
  def sha
143
- @sha ||= begin
144
- url = "https://api.github.com/repos/#{@repo}/commits/#{ref}"
145
- headers = ENV['GITHUB_TOKEN'] ? { "Authorization" => "token #{ENV['GITHUB_TOKEN']}" } : {}
146
- response = make_request(:Get, url, headers)
147
-
148
- case response
149
- when Net::HTTPOK
150
- body = JSON.parse(response.body)
151
- body['sha']
152
- when Net::HTTPUnauthorized
153
- raise Bolt::Error.new(
154
- "Invalid token at GITHUB_TOKEN, unable to resolve git modules.",
155
- "bolt/invalid-git-token-error"
156
- )
157
- when Net::HTTPForbidden
158
- message = "GitHub API rate limit exceeded, unable to resolve git modules. "
159
-
160
- unless ENV['GITHUB_TOKEN']
161
- message += "To increase your rate limit, set the GITHUB_TOKEN environment "\
162
- "variable with a GitHub personal access token."
163
- end
164
-
165
- raise Bolt::Error.new(message, 'bolt/github-api-rate-limit-error')
166
- when Net::HTTPNotFound
167
- raise Bolt::Error.new(invalid_git_msg(git), "bolt/missing-git-repository-error")
168
- else
108
+ id.sha
109
+ end
110
+
111
+ # Gets the ID for the module based on the specified ref and git URL.
112
+ # This is lazily resolved since Bolt does not always need this information,
113
+ # and requesting it is expensive.
114
+ #
115
+ private def id
116
+ @id ||= begin
117
+ # The request methods here return an ID object if the module name and SHA
118
+ # were found and nil otherwise. This lets Bolt try multiple methods for
119
+ # finding the module name and SHA, and short circuiting as soon as it does.
120
+ module_id = Bolt::ModuleInstaller::Specs::ID::GitHub.request(@git, @ref, @proxy) ||
121
+ Bolt::ModuleInstaller::Specs::ID::GitLab.request(@git, @ref, @proxy) ||
122
+ Bolt::ModuleInstaller::Specs::ID::GitClone.request(@git, @ref, @proxy)
123
+
124
+ unless module_id
169
125
  raise Bolt::Error.new(
170
- "Ref #{ref} at #{git} is not a commit, tag, or branch.",
171
- "bolt/invalid-git-ref-error"
126
+ "Unable to locate metadata and calculate SHA for ref #{@ref} at #{@git}. This may "\
127
+ "not be a valid module. For more information about how Bolt attempted to locate "\
128
+ "this information, check the debugging logs.",
129
+ 'bolt/missing-module-metadata-error'
172
130
  )
173
131
  end
132
+
133
+ module_id
174
134
  end
175
135
  end
176
136
 
177
- # Makes a generic HTTP request.
137
+ # Returns true if the URL is valid.
178
138
  #
179
- private def make_request(verb, url, headers = {})
180
- require 'net/http'
181
-
182
- uri = URI.parse(url)
183
- opts = { use_ssl: uri.scheme == 'https' }
139
+ private def valid_url?(url)
140
+ return true if url.start_with?('git@')
184
141
 
185
- Net::HTTP.start(uri.host, uri.port, opts) do |client|
186
- request = Net::HTTP.const_get(verb).new(uri, headers)
187
- client.request(request)
188
- end
189
- rescue StandardError => e
190
- raise Bolt::Error.new(
191
- "Failed to connect to #{uri}: #{e.message}",
192
- "bolt/http-connect-error"
193
- )
142
+ uri = URI.parse(url)
143
+ uri.is_a?(URI::HTTP) && uri.host
144
+ rescue URI::InvalidURIError
145
+ false
194
146
  end
195
147
  end
196
148
  end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'net/http'
5
+
6
+ require_relative '../../../../bolt/error'
7
+ require_relative '../../../../bolt/logger'
8
+
9
+ module Bolt
10
+ class ModuleInstaller
11
+ class Specs
12
+ class ID
13
+ class Base
14
+ attr_reader :name, :sha
15
+
16
+ # @param name [String] The module's name.
17
+ # @param sha [String] The ref's SHA1.
18
+ #
19
+ def initialize(name, sha)
20
+ @name = name
21
+ @sha = sha
22
+ end
23
+
24
+ # Request the name and SHA for a module and ref.
25
+ # This method must return either an ID object or nil. The GitSpec
26
+ # class relies on this class to return an ID object to indicate
27
+ # the module was found, or nil to indicate that it should try to
28
+ # find it another way (such as cloning the repo).
29
+ #
30
+ # @param git [String] The URL to the git repo.
31
+ # @param ref [String] The ref to checkout.
32
+ # @param proxy [String] A proxy to use when making requests.
33
+ #
34
+ def self.request(git, ref, proxy)
35
+ name, sha = name_and_sha(git, ref, proxy)
36
+ name && sha ? new(name, sha) : nil
37
+ end
38
+
39
+ # Stub method for retrieving the module's name and SHA. Must
40
+ # be implemented by all sub classes.
41
+ #
42
+ private_class_method def self.name_and_sha(_git, _ref, _proxy)
43
+ raise NotImplementedError, 'Class does not implemented #name_and_sha'
44
+ end
45
+
46
+ # Makes a HTTP request.
47
+ #
48
+ # @param url [String] The URL to make the request to.
49
+ # @param proxy [String] A proxy to use when making the request.
50
+ # @param headers [Hash] Headers to send with the request.
51
+ #
52
+ private_class_method def self.make_request(url, proxy, headers = {})
53
+ uri = URI.parse(url)
54
+ opts = { use_ssl: uri.scheme == 'https' }
55
+ args = [uri.host, uri.port]
56
+
57
+ if proxy
58
+ proxy = URI.parse(proxy)
59
+ args += [proxy.host, proxy.port, proxy.user, proxy.password]
60
+ end
61
+
62
+ Bolt::Logger.debug("Making request to #{loc(url, proxy)}")
63
+
64
+ Net::HTTP.start(*args, opts) do |client|
65
+ client.request(Net::HTTP::Get.new(uri, headers))
66
+ end
67
+ rescue StandardError => e
68
+ raise Bolt::Error.new(
69
+ "Failed to connect to #{loc(uri, proxy)}: #{e.message}",
70
+ "bolt/http-connect-error"
71
+ )
72
+ end
73
+
74
+ # Returns a formatted string describing the URL and proxy used when making
75
+ # a request.
76
+ #
77
+ # @param url [String, URI::HTTP] The URL used.
78
+ # @param proxy [String, URI::HTTP] The proxy used.
79
+ #
80
+ private_class_method def self.loc(url, proxy)
81
+ proxy ? "#{url} with proxy #{proxy}" : url.to_s
82
+ end
83
+
84
+ # Parses the metadata and validates that it is a Hash.
85
+ #
86
+ # @param metadata [String] The JSON data to parse.
87
+ #
88
+ private_class_method def self.parse_name_from_metadata(metadata)
89
+ metadata = JSON.parse(metadata)
90
+
91
+ unless metadata.is_a?(Hash)
92
+ raise Bolt::Error.new(
93
+ "Invalid metadata. Expected a Hash, got a #{metadata.class}: #{metadata}",
94
+ "bolt/invalid-module-metadata-error"
95
+ )
96
+ end
97
+
98
+ unless metadata.key?('name')
99
+ raise Bolt::Error.new(
100
+ "Invalid metadata. Metadata must include a 'name' key.",
101
+ "bolt/missing-module-name-error"
102
+ )
103
+ end
104
+
105
+ metadata['name']
106
+ rescue JSON::ParserError => e
107
+ raise Bolt::Error.new(
108
+ "Unable to parse metadata as JSON: #{e.message}",
109
+ "bolt/metadata-parse-error"
110
+ )
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../../bolt/module_installer/specs/id/base'
4
+
5
+ module Bolt
6
+ class ModuleInstaller
7
+ class Specs
8
+ class ID
9
+ class GitClone < Base
10
+ # Returns the name and SHA for the module at the given ref.
11
+ #
12
+ # @param git [String] The URL to the git repo.
13
+ # @param ref [String] The ref to checkout.
14
+ # @param proxy [String] The proxy to use when cloning.
15
+ #
16
+ private_class_method def self.name_and_sha(git, ref, proxy)
17
+ require 'open3'
18
+
19
+ unless git?
20
+ Bolt::Logger.debug("'git' executable not found, unable to use git clone resolution.")
21
+ return nil
22
+ end
23
+
24
+ # Clone the repo into a temp directory that will be automatically cleaned up.
25
+ Dir.mktmpdir do |dir|
26
+ return nil unless clone_repo(git, ref, dir, proxy)
27
+
28
+ # Extract the name from the metadata file and calculate the SHA.
29
+ Dir.chdir(dir) do
30
+ [request_name(git, ref), request_sha(git, ref)]
31
+ end
32
+ end
33
+ end
34
+
35
+ # Requests a module's metadata and returns the name from it.
36
+ #
37
+ # @param git [String] The URL to the git repo.
38
+ # @param ref [String] The ref to checkout.
39
+ #
40
+ private_class_method def self.request_name(git, ref)
41
+ command = %W[git show #{ref}:metadata.json]
42
+ Bolt::Logger.debug("Executing command '#{command.join(' ')}'")
43
+
44
+ out, err, status = Open3.capture3(*command)
45
+
46
+ unless status.success?
47
+ raise Bolt::Error.new(
48
+ "Unable to find metadata file at #{git}: #{err}",
49
+ "bolt/missing-metadata-file-error"
50
+ )
51
+ end
52
+
53
+ Bolt::Logger.debug("Found metadata file at #{git}")
54
+ parse_name_from_metadata(out)
55
+ end
56
+
57
+ # Requests the SHA for the specified ref.
58
+ #
59
+ # @param git [String] The URL to the git repo.
60
+ # @param ref [String] The ref to checkout.
61
+ #
62
+ private_class_method def self.request_sha(git, ref)
63
+ command = %W[git rev-parse #{ref}^{commit}]
64
+ Bolt::Logger.debug("Executing command '#{command.join(' ')}'")
65
+
66
+ out, err, status = Open3.capture3(*command)
67
+
68
+ if status.success?
69
+ out.strip
70
+ else
71
+ raise Bolt::Error.new(
72
+ "Unable to calculate SHA for ref #{ref} at #{git}: #{err}",
73
+ "bolt/invalid-ref-error"
74
+ )
75
+ end
76
+ end
77
+
78
+ # Clones the repository. First attempts to clone a bare repository
79
+ # and falls back to cloning the full repository if that fails. Cloning
80
+ # a bare repository is significantly faster for large modules, but
81
+ # cloning a bare repository using a commit is not supported.
82
+ #
83
+ # @param git [String] The URL to the git repo.
84
+ # @param ref [String] The ref to checkout.
85
+ # @param dir [String] The directory to clone the repo to.
86
+ # @param proxy [String] The proxy to use when cloning.
87
+ #
88
+ private_class_method def self.clone_repo(git, ref, dir, proxy)
89
+ clone = %W[git clone #{git} #{dir}]
90
+ clone += %W[--config "http.proxy=#{proxy}" --config "https.proxy=#{proxy}"] if proxy
91
+
92
+ bare_clone = clone + %w[--bare --depth=1]
93
+ bare_clone.push("--branch=#{ref}") unless ref == 'HEAD'
94
+
95
+ # Attempt to clone a bare repository
96
+ Bolt::Logger.debug("Executing command '#{bare_clone.join(' ')}'")
97
+ _out, err, status = Open3.capture3(*bare_clone)
98
+ return true if status.success?
99
+ Bolt::Logger.debug("Unable to clone bare repository at #{loc(git, proxy)}: #{err}")
100
+
101
+ # Fall back to cloning the full repository
102
+ Bolt::Logger.debug("Executing command '#{clone.join(' ')}'")
103
+ _out, err, status = Open3.capture3(*clone)
104
+ Bolt::Logger.debug("Unable to clone repository at #{loc(git, proxy)}: #{err}") unless status.success?
105
+ status.success?
106
+ end
107
+
108
+ # Returns true if the 'git' executable is available.
109
+ #
110
+ private_class_method def self.git?
111
+ Open3.capture3('git', '--version')
112
+ true
113
+ rescue Errno::ENOENT
114
+ false
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../../bolt/module_installer/specs/id/base'
4
+
5
+ module Bolt
6
+ class ModuleInstaller
7
+ class Specs
8
+ class ID
9
+ class GitHub < Base
10
+ # Returns the name and SHA for the module at the given ref.
11
+ #
12
+ # @param git [String] The URL to the git repo.
13
+ # @param ref [String] The ref to use.
14
+ # @param proxy [String] The proxy to use when making requests.
15
+ #
16
+ private_class_method def self.name_and_sha(git, ref, proxy)
17
+ repo = parse_repo(git)
18
+ return nil unless repo
19
+ [request_name(repo, ref, proxy), request_sha(repo, ref, proxy)]
20
+ end
21
+
22
+ # Parses the repo path out of the URL.
23
+ #
24
+ # @param git [String] The URL to the git repo.
25
+ #
26
+ private_class_method def self.parse_repo(git)
27
+ if git.start_with?('git@github.com:')
28
+ git.split('git@github.com:').last.split('.git').first
29
+ elsif git.start_with?('https://github.com')
30
+ git.split('https://github.com/').last.split('.git').first
31
+ end
32
+ end
33
+
34
+ # Requests a module's metadata and returns the name from it.
35
+ #
36
+ # @param repo [String] The repo ID, i.e. 'owner/repo'
37
+ # @param ref [String] The ref to use.
38
+ # @param proxy [String] The proxy to use when making requests.
39
+ #
40
+ private_class_method def self.request_name(repo, ref, proxy)
41
+ metadata_url = "https://raw.githubusercontent.com/#{repo}/#{ref}/metadata.json"
42
+ response = make_request(metadata_url, proxy)
43
+
44
+ case response
45
+ when Net::HTTPOK
46
+ Bolt::Logger.debug("Found metadata file at #{loc(metadata_url, proxy)}")
47
+ parse_name_from_metadata(response.body)
48
+ else
49
+ Bolt::Logger.debug("Unable to find metadata file at #{loc(metadata_url, proxy)}")
50
+ nil
51
+ end
52
+ end
53
+
54
+ # Requests the SHA for the specified ref.
55
+ #
56
+ # @param repo [String] The repo ID, i.e. 'owner/repo'
57
+ # @param ref [String] The ref to resolve.
58
+ # @param proxy [String] The proxy to use when making requests.
59
+ #
60
+ private_class_method def self.request_sha(repo, ref, proxy)
61
+ url = "https://api.github.com/repos/#{repo}/commits/#{ref}"
62
+ headers = ENV['GITHUB_TOKEN'] ? { "Authorization" => "token #{ENV['GITHUB_TOKEN']}" } : {}
63
+ response = make_request(url, proxy, headers)
64
+
65
+ case response
66
+ when Net::HTTPOK
67
+ JSON.parse(response.body).fetch('sha', nil)
68
+ when Net::HTTPUnauthorized
69
+ Bolt::Logger.debug("Invalid token at GITHUB_TOKEN, unable to calculate SHA.")
70
+ nil
71
+ when Net::HTTPForbidden
72
+ message = "GitHub API rate limit exceeded, unable to calculate SHA."
73
+
74
+ unless ENV['GITHUB_TOKEN']
75
+ message += " To increase your rate limit, set the GITHUB_TOKEN environment "\
76
+ "variable with a GitHub personal access token."
77
+ end
78
+
79
+ Bolt::Logger.debug(message)
80
+ nil
81
+ else
82
+ Bolt::Logger.debug("Unable to calculate SHA for ref #{ref}")
83
+ nil
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../../bolt/module_installer/specs/id/base'
4
+
5
+ module Bolt
6
+ class ModuleInstaller
7
+ class Specs
8
+ class ID
9
+ class GitLab < Base
10
+ # Returns the name and SHA for the module at the given ref.
11
+ #
12
+ # @param git [String] The URL to the git repo.
13
+ # @param ref [String] The ref to use.
14
+ # @param proxy [String] The proxy to use when making requests.
15
+ #
16
+ private_class_method def self.name_and_sha(git, ref, proxy)
17
+ repo = parse_repo(git)
18
+ return nil unless repo
19
+ [request_name(repo, ref, proxy), request_sha(repo, ref, proxy)]
20
+ end
21
+
22
+ # Parses the repo path out of the URL.
23
+ #
24
+ # @param git [String] The URL to the git repo.
25
+ #
26
+ private_class_method def self.parse_repo(git)
27
+ if git.start_with?('git@gitlab.com:')
28
+ git.split('git@gitlab.com:').last.split('.git').first
29
+ elsif git.start_with?('https://gitlab.com')
30
+ git.split('https://gitlab.com/').last.split('.git').first
31
+ end
32
+ end
33
+
34
+ # Requests a module's metadata and returns the name from it.
35
+ #
36
+ # @param repo [String] The repo ID, i.e. 'owner/repo'
37
+ # @param ref [String] The ref to use.
38
+ # @param proxy [String] The proxy to use when making requests.
39
+ #
40
+ private_class_method def self.request_name(repo, ref, proxy)
41
+ metadata_url = "https://gitlab.com/#{repo}/-/raw/#{ref}/metadata.json"
42
+ response = make_request(metadata_url, proxy)
43
+
44
+ case response
45
+ when Net::HTTPOK
46
+ Bolt::Logger.debug("Found metadata file at #{loc(metadata_url, proxy)}")
47
+ parse_name_from_metadata(response.body)
48
+ else
49
+ Bolt::Logger.debug("Unable to find metadata file at #{loc(metadata_url, proxy)}")
50
+ nil
51
+ end
52
+ end
53
+
54
+ # Requests the SHA for the specified ref.
55
+ #
56
+ # @param repo [String] The repo ID, i.e. 'owner/repo'
57
+ # @param ref [String] The ref to resolve.
58
+ # @param proxy [String] The proxy to use when making requests.
59
+ #
60
+ private_class_method def self.request_sha(repo, ref, proxy)
61
+ require 'cgi'
62
+
63
+ url = "https://gitlab.com/api/v4/projects/#{CGI.escape(repo)}/repository/commits/#{ref}"
64
+ headers = ENV['GITLAB_TOKEN'] ? { "PRIVATE-TOKEN" => ENV['GITLAB_TOKEN'] } : {}
65
+ response = make_request(url, proxy, headers)
66
+
67
+ case response
68
+ when Net::HTTPOK
69
+ JSON.parse(response.body).fetch('id', nil)
70
+ when Net::HTTPUnauthorized
71
+ Bolt::Logger.debug("Invalid token at GITLAB_TOKEN, unable to calculate SHA.")
72
+ nil
73
+ when Net::HTTPForbidden
74
+ message = "GitLab API rate limit exceeded, unable to calculate SHA."
75
+
76
+ unless ENV['GITLAB_TOKEN']
77
+ message += " To increase your rate limit, set the GITLAB_TOKEN environment "\
78
+ "variable with a GitLab personal access token."
79
+ end
80
+
81
+ Bolt::Logger.debug(message)
82
+ nil
83
+ else
84
+ Bolt::Logger.debug("Unable to calculate SHA for ref #{ref}")
85
+ nil
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -7,8 +7,10 @@ require_relative 'specs/git_spec'
7
7
  module Bolt
8
8
  class ModuleInstaller
9
9
  class Specs
10
- def initialize(specs = [])
11
- @specs = []
10
+ def initialize(specs = [], config = {})
11
+ @specs = []
12
+ @config = config
13
+
12
14
  add_specs(specs)
13
15
  assert_unique_names
14
16
  end
@@ -49,7 +51,7 @@ module Bolt
49
51
  #
50
52
  private def spec_from_hash(hash)
51
53
  return ForgeSpec.new(hash) if ForgeSpec.implements?(hash)
52
- return GitSpec.new(hash) if GitSpec.implements?(hash)
54
+ return GitSpec.new(hash, @config) if GitSpec.implements?(hash)
53
55
 
54
56
  raise Bolt::ValidationError, <<~MESSAGE.chomp
55
57
  Invalid module specification:
@@ -18,7 +18,7 @@ module Bolt
18
18
  # Adds a single module to the project.
19
19
  #
20
20
  def add(name, specs, puppetfile_path, moduledir, project_file, config)
21
- project_specs = Specs.new(specs)
21
+ project_specs = Specs.new(specs, config)
22
22
 
23
23
  # Exit early if project config already includes a spec with this name.
24
24
  if project_specs.include?(name)
@@ -151,7 +151,7 @@ module Bolt
151
151
  @outputter.print_message("Installing project modules\n\n")
152
152
 
153
153
  if resolve != false
154
- specs = Specs.new(specs)
154
+ specs = Specs.new(specs, config)
155
155
 
156
156
  # If forcibly installing or if there is no Puppetfile, resolve
157
157
  # and write a Puppetfile.