dependabot-terraform 0.76.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/helpers/build +19 -0
- data/lib/dependabot/terraform.rb +11 -0
- data/lib/dependabot/terraform/file_fetcher.rb +50 -0
- data/lib/dependabot/terraform/file_parser.rb +267 -0
- data/lib/dependabot/terraform/file_updater.rb +130 -0
- data/lib/dependabot/terraform/metadata_finder.rb +70 -0
- data/lib/dependabot/terraform/requirement.rb +34 -0
- data/lib/dependabot/terraform/requirements_updater.rb +139 -0
- data/lib/dependabot/terraform/update_checker.rb +197 -0
- data/lib/dependabot/terraform/version.rb +25 -0
- metadata +180 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 781b56819ef8fc54cb1b635f913f81139d149c80e6461d86b9e61e36916aa9c8
|
|
4
|
+
data.tar.gz: ed1697ae2b43e42d4b76db373643d28c3dff4c0f67c07291f8180fc56da0c341
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: b51f5746555c34bffed4736fa11217c8b24e8fb1d4f464846c750572e9e55f77680231b0d2603555f06ae14b35dc34bcc16808cd9740566c0d7ae24a2951900e
|
|
7
|
+
data.tar.gz: 01cbd90a18f476d9c7521c7facc5e225e1c2afd3b8954d4f0efb88daf69d370630d1a840b6eccbdd0add3c2fa277d46aa44935e6c6fd673d70dbe0b174f2cb54
|
data/helpers/build
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
set -e
|
|
4
|
+
|
|
5
|
+
install_dir=$1
|
|
6
|
+
if [ -z "$install_dir" ]; then
|
|
7
|
+
echo "usage: $0 INSTALL_DIR"
|
|
8
|
+
exit 1
|
|
9
|
+
fi
|
|
10
|
+
|
|
11
|
+
if [ ! -d "$install_dir/bin" ]; then
|
|
12
|
+
mkdir -p "$install_dir/bin"
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
os="$(uname -s | tr '[:upper:]' '[:lower:]')"
|
|
16
|
+
github_url="https://github.com/kvz/json2hcl"
|
|
17
|
+
url="${github_url}/releases/download/v0.0.6/json2hcl_v0.0.6_${os}_amd64"
|
|
18
|
+
wget -O "$install_dir/bin/json2hcl" "$url"
|
|
19
|
+
chmod +x "$install_dir/bin/json2hcl"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# These all need to be required so the various classes can be registered in a
|
|
4
|
+
# lookup table of package manager names to concrete classes.
|
|
5
|
+
require "dependabot/terraform/file_fetcher"
|
|
6
|
+
require "dependabot/terraform/file_parser"
|
|
7
|
+
require "dependabot/terraform/update_checker"
|
|
8
|
+
require "dependabot/terraform/file_updater"
|
|
9
|
+
require "dependabot/terraform/metadata_finder"
|
|
10
|
+
require "dependabot/terraform/requirement"
|
|
11
|
+
require "dependabot/terraform/version"
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dependabot/file_fetchers"
|
|
4
|
+
require "dependabot/file_fetchers/base"
|
|
5
|
+
|
|
6
|
+
module Dependabot
|
|
7
|
+
module Terraform
|
|
8
|
+
class FileFetcher < Dependabot::FileFetchers::Base
|
|
9
|
+
def self.required_files_in?(filenames)
|
|
10
|
+
filenames.any? { |f| f.end_with?(".tf", ".tfvars") }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.required_files_message
|
|
14
|
+
"Repo must contain a Terraform configuration file."
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def fetch_files
|
|
20
|
+
fetched_files = []
|
|
21
|
+
fetched_files += terraform_files
|
|
22
|
+
fetched_files += terragrunt_files
|
|
23
|
+
|
|
24
|
+
return fetched_files if fetched_files.any?
|
|
25
|
+
|
|
26
|
+
raise(
|
|
27
|
+
Dependabot::DependencyFileNotFound,
|
|
28
|
+
File.join(directory, "<anything>.tf")
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def terraform_files
|
|
33
|
+
@terraform_files ||=
|
|
34
|
+
repo_contents(raise_errors: false).
|
|
35
|
+
select { |f| f.type == "file" && f.name.end_with?(".tf") }.
|
|
36
|
+
map { |f| fetch_file_from_host(f.name) }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def terragrunt_files
|
|
40
|
+
@terragrunt_files ||=
|
|
41
|
+
repo_contents(raise_errors: false).
|
|
42
|
+
select { |f| f.type == "file" && f.name.end_with?(".tfvars") }.
|
|
43
|
+
map { |f| fetch_file_from_host(f.name) }
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
Dependabot::FileFetchers.
|
|
50
|
+
register("terraform", Dependabot::Terraform::FileFetcher)
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "cgi"
|
|
4
|
+
require "excon"
|
|
5
|
+
require "nokogiri"
|
|
6
|
+
require "dependabot/dependency"
|
|
7
|
+
require "dependabot/file_parsers"
|
|
8
|
+
require "dependabot/file_parsers/base"
|
|
9
|
+
require "dependabot/git_commit_checker"
|
|
10
|
+
require "dependabot/shared_helpers"
|
|
11
|
+
require "dependabot/errors"
|
|
12
|
+
|
|
13
|
+
module Dependabot
|
|
14
|
+
module Terraform
|
|
15
|
+
class FileParser < Dependabot::FileParsers::Base
|
|
16
|
+
require "dependabot/file_parsers/base/dependency_set"
|
|
17
|
+
|
|
18
|
+
ARCHIVE_EXTENSIONS = %w(.zip .tbz2 .tgz .txz).freeze
|
|
19
|
+
|
|
20
|
+
def parse
|
|
21
|
+
dependency_set = DependencySet.new
|
|
22
|
+
|
|
23
|
+
terraform_files.each do |file|
|
|
24
|
+
modules = parsed_file(file).fetch("module", []).map(&:first)
|
|
25
|
+
modules.each do |name, details|
|
|
26
|
+
dependency_set << build_terraform_dependency(file, name, details)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
terragrunt_files.each do |file|
|
|
31
|
+
modules = parsed_file(file).fetch("terragrunt", []).first || {}
|
|
32
|
+
modules = modules.fetch("terraform", [])
|
|
33
|
+
modules.each do |details|
|
|
34
|
+
next unless details["source"]
|
|
35
|
+
|
|
36
|
+
dependency_set << build_terragrunt_dependency(file, details)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
dependency_set.dependencies
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def build_terraform_dependency(file, name, details)
|
|
46
|
+
details = details.first
|
|
47
|
+
|
|
48
|
+
source = source_from(details)
|
|
49
|
+
dep_name =
|
|
50
|
+
source[:type] == "registry" ? source[:module_identifier] : name
|
|
51
|
+
version_req = details["version"]&.strip
|
|
52
|
+
version =
|
|
53
|
+
if source[:type] == "git" then version_from_ref(source[:ref])
|
|
54
|
+
elsif version_req&.match?(/^\d/) then version_req
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
Dependency.new(
|
|
58
|
+
name: dep_name,
|
|
59
|
+
version: version,
|
|
60
|
+
package_manager: "terraform",
|
|
61
|
+
requirements: [
|
|
62
|
+
requirement: version_req,
|
|
63
|
+
groups: [],
|
|
64
|
+
file: file.name,
|
|
65
|
+
source: source
|
|
66
|
+
]
|
|
67
|
+
)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def build_terragrunt_dependency(file, details)
|
|
71
|
+
source = source_from(details)
|
|
72
|
+
dep_name =
|
|
73
|
+
if Source.from_url(source[:url])
|
|
74
|
+
Source.from_url(source[:url]).repo
|
|
75
|
+
else
|
|
76
|
+
source[:url]
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
version = version_from_ref(source[:ref])
|
|
80
|
+
|
|
81
|
+
Dependency.new(
|
|
82
|
+
name: dep_name,
|
|
83
|
+
version: version,
|
|
84
|
+
package_manager: "terraform",
|
|
85
|
+
requirements: [
|
|
86
|
+
requirement: nil,
|
|
87
|
+
groups: [],
|
|
88
|
+
file: file.name,
|
|
89
|
+
source: source
|
|
90
|
+
]
|
|
91
|
+
)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Full docs at https://www.terraform.io/docs/modules/sources.html
|
|
95
|
+
def source_from(details_hash)
|
|
96
|
+
raw_source = details_hash.fetch("source")
|
|
97
|
+
bare_source = get_proxied_source(raw_source)
|
|
98
|
+
|
|
99
|
+
source_details =
|
|
100
|
+
case source_type(bare_source)
|
|
101
|
+
when :http_archive, :path, :mercurial, :s3
|
|
102
|
+
{ type: source_type(bare_source).to_s, url: bare_source }
|
|
103
|
+
when :github, :bitbucket, :git
|
|
104
|
+
git_source_details_from(bare_source)
|
|
105
|
+
when :registry
|
|
106
|
+
registry_source_details_from(bare_source)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
source_details[:proxy_url] = raw_source if raw_source != bare_source
|
|
110
|
+
source_details
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def registry_source_details_from(source_string)
|
|
114
|
+
parts = source_string.split("/")
|
|
115
|
+
|
|
116
|
+
if parts.count == 3
|
|
117
|
+
{
|
|
118
|
+
type: "registry",
|
|
119
|
+
registry_hostname: "registry.terraform.io",
|
|
120
|
+
module_identifier: source_string
|
|
121
|
+
}
|
|
122
|
+
elsif parts.count == 4
|
|
123
|
+
{
|
|
124
|
+
type: "registry",
|
|
125
|
+
registry_hostname: parts.first,
|
|
126
|
+
module_identifier: parts[1..3].join("/")
|
|
127
|
+
}
|
|
128
|
+
else
|
|
129
|
+
msg = "Invalid registry source specified: '#{source_string}'"
|
|
130
|
+
raise DependencyFileNotEvaluatable, msg
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def git_source_details_from(source_string)
|
|
135
|
+
git_url = source_string.strip.gsub(/^git::/, "")
|
|
136
|
+
unless git_url.start_with?("git@") || git_url.include?("://")
|
|
137
|
+
git_url = "https://" + git_url
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
bare_uri =
|
|
141
|
+
if git_url.include?("git@")
|
|
142
|
+
git_url.split("git@").last.sub(":", "/")
|
|
143
|
+
else
|
|
144
|
+
git_url.sub(%r{.*?://}, "")
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
querystr = URI.parse("https://" + bare_uri).query
|
|
148
|
+
git_url = git_url.split(%r{(?<!:)//}).first.gsub("?#{querystr}", "")
|
|
149
|
+
|
|
150
|
+
{
|
|
151
|
+
type: "git",
|
|
152
|
+
url: git_url,
|
|
153
|
+
branch: nil,
|
|
154
|
+
ref: CGI.parse(querystr.to_s)["ref"].first
|
|
155
|
+
}
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def version_from_ref(ref)
|
|
159
|
+
version_regex = GitCommitChecker::VERSION_REGEX
|
|
160
|
+
return unless ref&.match?(version_regex)
|
|
161
|
+
|
|
162
|
+
ref.match(version_regex).named_captures.fetch("version")
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# See https://www.terraform.io/docs/modules/sources.html#http-urls for
|
|
166
|
+
# details of how Terraform handle HTTP(S) sources for modules
|
|
167
|
+
def get_proxied_source(raw_source)
|
|
168
|
+
return raw_source unless raw_source.start_with?("http")
|
|
169
|
+
|
|
170
|
+
uri = URI.parse(raw_source.split(%r{(?<!:)//}).first)
|
|
171
|
+
return raw_source if uri.path.end_with?(*ARCHIVE_EXTENSIONS)
|
|
172
|
+
return raw_source if URI.parse(raw_source).query.include?("archive=")
|
|
173
|
+
|
|
174
|
+
url = raw_source.split(%r{(?<!:)//}).first + "?terraform-get=1"
|
|
175
|
+
|
|
176
|
+
response = Excon.get(
|
|
177
|
+
url,
|
|
178
|
+
idempotent: true,
|
|
179
|
+
**SharedHelpers.excon_defaults
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
if response.headers["X-Terraform-Get"]
|
|
183
|
+
return response.headers["X-Terraform-Get"]
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
doc = Nokogiri::XML(response.body)
|
|
187
|
+
doc.css("meta").find do |tag|
|
|
188
|
+
tag.attributes&.fetch("name", nil)&.value == "terraform-get"
|
|
189
|
+
end&.attributes&.fetch("content", nil)&.value
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
|
193
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
|
194
|
+
def source_type(source_string)
|
|
195
|
+
return :path if source_string.start_with?(".")
|
|
196
|
+
return :github if source_string.start_with?("github.com/")
|
|
197
|
+
return :bitbucket if source_string.start_with?("bitbucket.org/")
|
|
198
|
+
return :git if source_string.start_with?("git::")
|
|
199
|
+
return :mercurial if source_string.start_with?("hg::")
|
|
200
|
+
return :s3 if source_string.start_with?("s3::")
|
|
201
|
+
|
|
202
|
+
if source_string.split("/").first.include?("::")
|
|
203
|
+
raise "Unknown src: #{source_string}"
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
return :registry unless source_string.start_with?("http")
|
|
207
|
+
|
|
208
|
+
path_uri = URI.parse(source_string.split(%r{(?<!:)//}).first)
|
|
209
|
+
query_uri = URI.parse(source_string)
|
|
210
|
+
return :http_archive if path_uri.path.end_with?(*ARCHIVE_EXTENSIONS)
|
|
211
|
+
return :http_archive if query_uri.query.include?("archive=")
|
|
212
|
+
|
|
213
|
+
raise "HTTP source, but not an archive!"
|
|
214
|
+
end
|
|
215
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
|
216
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
|
217
|
+
|
|
218
|
+
def parsed_file(file)
|
|
219
|
+
@parsed_buildfile ||= {}
|
|
220
|
+
@parsed_buildfile[file.name] ||=
|
|
221
|
+
SharedHelpers.in_a_temporary_directory do
|
|
222
|
+
File.write("tmp.tf", file.content)
|
|
223
|
+
|
|
224
|
+
command = "#{terraform_parser_path} -reverse < tmp.tf"
|
|
225
|
+
raw_response = nil
|
|
226
|
+
IO.popen(command) { |process| raw_response = process.read }
|
|
227
|
+
|
|
228
|
+
unless $CHILD_STATUS.success?
|
|
229
|
+
raise SharedHelpers::HelperSubprocessFailed.new(
|
|
230
|
+
raw_response,
|
|
231
|
+
command
|
|
232
|
+
)
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
JSON.parse(raw_response)
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def terraform_parser_path
|
|
240
|
+
helper_bin_dir = File.join(native_helpers_root, "terraform/bin")
|
|
241
|
+
Pathname.new(File.join(helper_bin_dir, "json2hcl")).cleanpath.to_path
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def native_helpers_root
|
|
245
|
+
default_path = File.join(__dir__, "../../../helpers/install-dir")
|
|
246
|
+
ENV.fetch("DEPENDABOT_NATIVE_HELPERS_PATH", default_path)
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def terraform_files
|
|
250
|
+
dependency_files.select { |f| f.name.end_with?(".tf") }
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def terragrunt_files
|
|
254
|
+
dependency_files.select { |f| f.name.end_with?(".tfvars") }
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def check_required_files
|
|
258
|
+
return if [*terraform_files, *terragrunt_files].any?
|
|
259
|
+
|
|
260
|
+
raise "No Terraform configuration file!"
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
Dependabot::FileParsers.
|
|
267
|
+
register("terraform", Dependabot::Terraform::FileParser)
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dependabot/file_updaters"
|
|
4
|
+
require "dependabot/file_updaters/base"
|
|
5
|
+
require "dependabot/errors"
|
|
6
|
+
|
|
7
|
+
module Dependabot
|
|
8
|
+
module Terraform
|
|
9
|
+
class FileUpdater < Dependabot::FileUpdaters::Base
|
|
10
|
+
def self.updated_files_regex
|
|
11
|
+
[/\.tf$/, /\.tfvars$/]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def updated_dependency_files
|
|
15
|
+
updated_files = []
|
|
16
|
+
|
|
17
|
+
[*terraform_files, *terragrunt_files].each do |file|
|
|
18
|
+
next unless file_changed?(file)
|
|
19
|
+
|
|
20
|
+
updated_content = updated_terraform_file_content(file)
|
|
21
|
+
raise "Content didn't change!" if updated_content == file.content
|
|
22
|
+
|
|
23
|
+
updated_files << updated_file(file: file, content: updated_content)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
raise "No files changed!" if updated_files.none?
|
|
27
|
+
|
|
28
|
+
updated_files
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def updated_terraform_file_content(file)
|
|
34
|
+
content = file.content.dup
|
|
35
|
+
|
|
36
|
+
reqs = dependency.requirements.zip(dependency.previous_requirements).
|
|
37
|
+
reject { |new_req, old_req| new_req == old_req }
|
|
38
|
+
|
|
39
|
+
# Loop through each changed requirement and update the files
|
|
40
|
+
reqs.each do |new_req, old_req|
|
|
41
|
+
raise "Bad req match" unless new_req[:file] == old_req[:file]
|
|
42
|
+
next unless new_req.fetch(:file) == file.name
|
|
43
|
+
|
|
44
|
+
case new_req[:source][:type]
|
|
45
|
+
when "git"
|
|
46
|
+
update_git_declaration(new_req, old_req, content, file.name)
|
|
47
|
+
when "registry"
|
|
48
|
+
update_registry_declaration(new_req, old_req, content)
|
|
49
|
+
else
|
|
50
|
+
raise "Don't know how to update a #{new_req[:source][:type]} "\
|
|
51
|
+
"declaration!"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
content
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def update_git_declaration(new_req, old_req, updated_content, filename)
|
|
59
|
+
url = old_req.fetch(:source)[:url].gsub(%r{^https://}, "")
|
|
60
|
+
tag = old_req.fetch(:source)[:ref]
|
|
61
|
+
url_regex = /#{Regexp.quote(url)}.*ref=#{Regexp.quote(tag)}/
|
|
62
|
+
|
|
63
|
+
declaration_regex = git_declaration_regex(filename)
|
|
64
|
+
|
|
65
|
+
updated_content.sub!(declaration_regex) do |regex_match|
|
|
66
|
+
regex_match.sub(url_regex) do |url_match|
|
|
67
|
+
url_match.sub(old_req[:source][:ref], new_req[:source][:ref])
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def update_registry_declaration(new_req, old_req, updated_content)
|
|
73
|
+
updated_content.sub!(registry_declaration_regex) do |regex_match|
|
|
74
|
+
regex_match.sub(/version\s*=.*/) do |req_line_match|
|
|
75
|
+
req_line_match.sub(old_req[:requirement], new_req[:requirement])
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def dependency
|
|
81
|
+
# Terraform updates will only ever be updating a single dependency
|
|
82
|
+
dependencies.first
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def files_with_requirement
|
|
86
|
+
filenames = dependency.requirements.map { |r| r[:file] }
|
|
87
|
+
dependency_files.select { |file| filenames.include?(file.name) }
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def terraform_files
|
|
91
|
+
dependency_files.select { |f| f.name.end_with?(".tf") }
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def terragrunt_files
|
|
95
|
+
dependency_files.select { |f| f.name.end_with?(".tfvars") }
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def check_required_files
|
|
99
|
+
return if [*terraform_files, *terragrunt_files].any?
|
|
100
|
+
|
|
101
|
+
raise "No Terraform configuration file!"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def registry_declaration_regex
|
|
105
|
+
/
|
|
106
|
+
(?<=\{)
|
|
107
|
+
(?:(?!^\}).)*
|
|
108
|
+
source\s*=\s*["']#{Regexp.escape(dependency.name)}["']
|
|
109
|
+
(?:(?!^\}).)*
|
|
110
|
+
/mx
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def git_declaration_regex(filename)
|
|
114
|
+
# For terragrunt dependencies there's not a lot we can base the
|
|
115
|
+
# regex on. Just look for declarations within a `terraform` block
|
|
116
|
+
return /terraform\s*\{(?:(?!^\}).)*/m if filename.end_with?(".tfvars")
|
|
117
|
+
|
|
118
|
+
# For modules we can do better - filter for module blocks that use the
|
|
119
|
+
# name of the dependency
|
|
120
|
+
/
|
|
121
|
+
module\s+["']#{Regexp.escape(dependency.name)}["']\s*\{
|
|
122
|
+
(?:(?!^\}).)*
|
|
123
|
+
/mx
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
Dependabot::FileUpdaters.
|
|
130
|
+
register("terraform", Dependabot::Terraform::FileUpdater)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "excon"
|
|
4
|
+
require "json"
|
|
5
|
+
require "dependabot/metadata_finders"
|
|
6
|
+
require "dependabot/metadata_finders/base"
|
|
7
|
+
require "dependabot/shared_helpers"
|
|
8
|
+
|
|
9
|
+
module Dependabot
|
|
10
|
+
module Terraform
|
|
11
|
+
class MetadataFinder < Dependabot::MetadataFinders::Base
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def look_up_source
|
|
15
|
+
case new_source_type
|
|
16
|
+
when "git" then find_source_from_git_url
|
|
17
|
+
when "registry" then find_source_from_registry_details
|
|
18
|
+
else raise "Unexpected source type: #{new_source_type}"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def new_source_type
|
|
23
|
+
sources =
|
|
24
|
+
dependency.requirements.map { |r| r.fetch(:source) }.uniq.compact
|
|
25
|
+
|
|
26
|
+
return "default" if sources.empty?
|
|
27
|
+
raise "Multiple sources! #{sources.join(', ')}" if sources.count > 1
|
|
28
|
+
|
|
29
|
+
sources.first[:type] || sources.first.fetch("type")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def find_source_from_git_url
|
|
33
|
+
info = dependency.requirements.map { |r| r[:source] }.compact.first
|
|
34
|
+
|
|
35
|
+
url = info[:url] || info.fetch("url")
|
|
36
|
+
Source.from_url(url)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Registry API docs:
|
|
40
|
+
# https://www.terraform.io/docs/registry/api.html
|
|
41
|
+
def find_source_from_registry_details
|
|
42
|
+
info = dependency.requirements.map { |r| r[:source] }.compact.first
|
|
43
|
+
|
|
44
|
+
hostname = info[:registry_hostname] || info["registry_hostname"]
|
|
45
|
+
|
|
46
|
+
# TODO: Implement service discovery for custom registries
|
|
47
|
+
return unless hostname == "registry.terraform.io"
|
|
48
|
+
|
|
49
|
+
url = "https://registry.terraform.io/v1/modules/"\
|
|
50
|
+
"#{dependency.name}/#{dependency.version}"
|
|
51
|
+
|
|
52
|
+
response = Excon.get(
|
|
53
|
+
url,
|
|
54
|
+
idempotent: true,
|
|
55
|
+
**SharedHelpers.excon_defaults
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
unless response.status == 200
|
|
59
|
+
raise "Response from registry was #{response.status}"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
source_url = JSON.parse(response.body).fetch("source")
|
|
63
|
+
Source.from_url(source_url) if source_url
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
Dependabot::MetadataFinders.
|
|
70
|
+
register("terraform", Dependabot::Terraform::MetadataFinder)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dependabot/utils"
|
|
4
|
+
require "dependabot/terraform/version"
|
|
5
|
+
|
|
6
|
+
# Just ensures that Terraform requirements use Terraform versions
|
|
7
|
+
module Dependabot
|
|
8
|
+
module Terraform
|
|
9
|
+
class Requirement < Gem::Requirement
|
|
10
|
+
def self.parse(obj)
|
|
11
|
+
return ["=", Version.new(obj.to_s)] if obj.is_a?(Gem::Version)
|
|
12
|
+
|
|
13
|
+
unless (matches = PATTERN.match(obj.to_s))
|
|
14
|
+
msg = "Illformed requirement [#{obj.inspect}]"
|
|
15
|
+
raise BadRequirementError, msg
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
return DefaultRequirement if matches[1] == ">=" && matches[2] == "0"
|
|
19
|
+
|
|
20
|
+
[matches[1] || "=", Terraform::Version.new(matches[2])]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# For consistency with other langauges, we define a requirements array.
|
|
24
|
+
# Terraform doesn't have an `OR` separator for requirements, so it
|
|
25
|
+
# always contains a single element.
|
|
26
|
+
def self.requirements_array(requirement_string)
|
|
27
|
+
[new(requirement_string)]
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
Dependabot::Utils.
|
|
34
|
+
register_requirement_class("terraform", Dependabot::Terraform::Requirement)
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
####################################################################
|
|
4
|
+
# For more details on Terraform version constraints, see: #
|
|
5
|
+
# https://www.terraform.io/docs/modules/usage.html#module-versions #
|
|
6
|
+
####################################################################
|
|
7
|
+
|
|
8
|
+
require "dependabot/terraform/version"
|
|
9
|
+
require "dependabot/terraform/requirement"
|
|
10
|
+
|
|
11
|
+
module Dependabot
|
|
12
|
+
module Terraform
|
|
13
|
+
class RequirementsUpdater
|
|
14
|
+
def initialize(requirements:, latest_version:,
|
|
15
|
+
tag_for_latest_version:)
|
|
16
|
+
@requirements = requirements
|
|
17
|
+
@tag_for_latest_version = tag_for_latest_version
|
|
18
|
+
|
|
19
|
+
return unless latest_version
|
|
20
|
+
return unless version_class.correct?(latest_version)
|
|
21
|
+
|
|
22
|
+
@latest_version = version_class.new(latest_version)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def updated_requirements
|
|
26
|
+
return requirements unless latest_version
|
|
27
|
+
|
|
28
|
+
# Note: Order is important here. The FileUpdater needs the updated
|
|
29
|
+
# requirement at index `i` to correspond to the previous requirement
|
|
30
|
+
# at the same index.
|
|
31
|
+
requirements.map do |req|
|
|
32
|
+
case req.dig(:source, :type)
|
|
33
|
+
when "git" then update_git_requirement(req)
|
|
34
|
+
when "registry" then update_registry_requirement(req)
|
|
35
|
+
else req
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
attr_reader :requirements, :latest_version, :tag_for_latest_version
|
|
43
|
+
|
|
44
|
+
def update_git_requirement(req)
|
|
45
|
+
return req unless req.dig(:source, :ref)
|
|
46
|
+
return req unless tag_for_latest_version
|
|
47
|
+
|
|
48
|
+
req.merge(source: req[:source].merge(ref: tag_for_latest_version))
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def update_registry_requirement(req)
|
|
52
|
+
return req if req.fetch(:requirement).nil?
|
|
53
|
+
|
|
54
|
+
string_req = req.fetch(:requirement).strip
|
|
55
|
+
ruby_req = requirement_class.new(string_req)
|
|
56
|
+
return req if ruby_req.satisfied_by?(latest_version)
|
|
57
|
+
|
|
58
|
+
new_req =
|
|
59
|
+
if ruby_req.exact? then latest_version.to_s
|
|
60
|
+
elsif string_req.start_with?("~>")
|
|
61
|
+
update_twiddle_version(string_req).to_s
|
|
62
|
+
else
|
|
63
|
+
update_range(string_req).map(&:to_s).join(", ")
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
req.merge(requirement: new_req)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Updates the version in a "~>" constraint to allow the given version
|
|
70
|
+
def update_twiddle_version(req_string)
|
|
71
|
+
old_version = requirement_class.new(req_string).
|
|
72
|
+
requirements.first.last
|
|
73
|
+
updated_version = at_same_precision(latest_version, old_version)
|
|
74
|
+
req_string.sub(old_version.to_s, updated_version)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def update_range(req_string)
|
|
78
|
+
requirement_class.new(req_string).requirements.flat_map do |r|
|
|
79
|
+
next r if r.satisfied_by?(latest_version)
|
|
80
|
+
|
|
81
|
+
case op = r.requirements.first.first
|
|
82
|
+
when "<", "<=" then [update_greatest_version(r, latest_version)]
|
|
83
|
+
when "!=" then []
|
|
84
|
+
else raise "Unexpected operation for unsatisfied req: #{op}"
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def at_same_precision(new_version, old_version)
|
|
90
|
+
release_precision =
|
|
91
|
+
old_version.to_s.split(".").select { |i| i.match?(/^\d+$/) }.count
|
|
92
|
+
prerelease_precision =
|
|
93
|
+
old_version.to_s.split(".").count - release_precision
|
|
94
|
+
|
|
95
|
+
new_release =
|
|
96
|
+
new_version.to_s.split(".").first(release_precision)
|
|
97
|
+
new_prerelease =
|
|
98
|
+
new_version.to_s.split(".").
|
|
99
|
+
drop_while { |i| i.match?(/^\d+$/) }.
|
|
100
|
+
first([prerelease_precision, 1].max)
|
|
101
|
+
|
|
102
|
+
[*new_release, *new_prerelease].join(".")
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Updates the version in a "<" or "<=" constraint to allow the given
|
|
106
|
+
# version
|
|
107
|
+
def update_greatest_version(requirement, version_to_be_permitted)
|
|
108
|
+
if version_to_be_permitted.is_a?(String)
|
|
109
|
+
version_to_be_permitted =
|
|
110
|
+
version_class.new(version_to_be_permitted)
|
|
111
|
+
end
|
|
112
|
+
op, version = requirement.requirements.first
|
|
113
|
+
version = version.release if version.prerelease?
|
|
114
|
+
|
|
115
|
+
index_to_update =
|
|
116
|
+
version.segments.map.with_index { |seg, i| seg.zero? ? 0 : i }.max
|
|
117
|
+
|
|
118
|
+
new_segments = version.segments.map.with_index do |_, index|
|
|
119
|
+
if index < index_to_update
|
|
120
|
+
version_to_be_permitted.segments[index]
|
|
121
|
+
elsif index == index_to_update
|
|
122
|
+
version_to_be_permitted.segments[index] + 1
|
|
123
|
+
else 0
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
requirement_class.new("#{op} #{new_segments.join('.')}")
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def version_class
|
|
131
|
+
Version
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def requirement_class
|
|
135
|
+
Requirement
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dependabot/update_checkers"
|
|
4
|
+
require "dependabot/update_checkers/base"
|
|
5
|
+
require "dependabot/git_commit_checker"
|
|
6
|
+
require "dependabot/terraform/requirements_updater"
|
|
7
|
+
require "dependabot/terraform/requirement"
|
|
8
|
+
require "dependabot/terraform/version"
|
|
9
|
+
|
|
10
|
+
module Dependabot
|
|
11
|
+
module Terraform
|
|
12
|
+
class UpdateChecker < Dependabot::UpdateCheckers::Base
|
|
13
|
+
def latest_version
|
|
14
|
+
return latest_version_for_git_dependency if git_dependency?
|
|
15
|
+
return latest_version_for_registry_dependency if registry_dependency?
|
|
16
|
+
# Other sources (mercurial, path dependencies) just return `nil`
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def latest_resolvable_version
|
|
20
|
+
# No concept of resolvability for terraform modules (that we're aware
|
|
21
|
+
# of - there may be in future).
|
|
22
|
+
latest_version
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def latest_resolvable_version_with_no_unlock
|
|
26
|
+
# Irrelevant, since Terraform doesn't have a lockfile
|
|
27
|
+
nil
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def updated_requirements
|
|
31
|
+
RequirementsUpdater.new(
|
|
32
|
+
requirements: dependency.requirements,
|
|
33
|
+
latest_version: latest_version&.to_s,
|
|
34
|
+
tag_for_latest_version: tag_for_latest_version
|
|
35
|
+
).updated_requirements
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def requirements_unlocked_or_can_be?
|
|
39
|
+
# If the requirement comes from a proxy URL then there's no way for
|
|
40
|
+
# us to update it
|
|
41
|
+
!proxy_requirement?
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def requirement_class
|
|
45
|
+
Requirement
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def version_class
|
|
49
|
+
Version
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def latest_version_resolvable_with_full_unlock?
|
|
55
|
+
# Full unlock checks aren't relevant for Terraform files
|
|
56
|
+
false
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def updated_dependencies_after_full_unlock
|
|
60
|
+
raise NotImplementedError
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def latest_version_for_registry_dependency
|
|
64
|
+
return unless registry_dependency?
|
|
65
|
+
|
|
66
|
+
if @latest_version_for_registry_dependency
|
|
67
|
+
return @latest_version_for_registry_dependency
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
versions = all_registry_versions
|
|
71
|
+
versions.reject!(&:prerelease?) unless wants_prerelease?
|
|
72
|
+
versions.reject! { |v| ignore_reqs.any? { |r| r.satisfied_by?(v) } }
|
|
73
|
+
|
|
74
|
+
@latest_version_for_registry_dependency = versions.max
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def all_registry_versions
|
|
78
|
+
hostname = dependency_source_details.fetch(:registry_hostname)
|
|
79
|
+
identifier = dependency_source_details.fetch(:module_identifier)
|
|
80
|
+
|
|
81
|
+
# TODO: Implement service discovery for custom registries
|
|
82
|
+
return unless hostname == "registry.terraform.io"
|
|
83
|
+
|
|
84
|
+
url = "https://registry.terraform.io/v1/modules/"\
|
|
85
|
+
"#{identifier}/versions"
|
|
86
|
+
|
|
87
|
+
response = Excon.get(
|
|
88
|
+
url,
|
|
89
|
+
idempotent: true,
|
|
90
|
+
**SharedHelpers.excon_defaults
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
unless response.status == 200
|
|
94
|
+
raise "Response from registry was #{response.status}"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
JSON.parse(response.body).
|
|
98
|
+
fetch("modules").first.fetch("versions").
|
|
99
|
+
map { |release| version_class.new(release.fetch("version")) }
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def wants_prerelease?
|
|
103
|
+
current_version = dependency.version
|
|
104
|
+
if current_version &&
|
|
105
|
+
version_class.correct?(current_version) &&
|
|
106
|
+
version_class.new(current_version).prerelease?
|
|
107
|
+
return true
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
dependency.requirements.any? do |req|
|
|
111
|
+
req[:requirement]&.match?(/\d-[A-Za-z0-9]/)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def latest_version_for_git_dependency
|
|
116
|
+
# If the module isn't pinned then there's nothing for us to update
|
|
117
|
+
# (since there's no lockfile to update the version in). We still
|
|
118
|
+
# return the latest commit for the given branch, in order to keep
|
|
119
|
+
# this method consistent
|
|
120
|
+
unless git_commit_checker.pinned?
|
|
121
|
+
return git_commit_checker.head_commit_for_current_branch
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# If the dependency is pinned to a tag that looks like a version then
|
|
125
|
+
# we want to update that tag. Because we don't have a lockfile, the
|
|
126
|
+
# latest version is the tag itself.
|
|
127
|
+
if git_commit_checker.pinned_ref_looks_like_version?
|
|
128
|
+
latest_tag = git_commit_checker.local_tag_for_latest_version&.
|
|
129
|
+
fetch(:tag)
|
|
130
|
+
version_rgx = GitCommitChecker::VERSION_REGEX
|
|
131
|
+
return unless latest_tag.match(version_rgx)
|
|
132
|
+
|
|
133
|
+
version = latest_tag.match(version_rgx).
|
|
134
|
+
named_captures.fetch("version")
|
|
135
|
+
return version_class.new(version)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# If the dependency is pinned to a tag that doesn't look like a
|
|
139
|
+
# version then there's nothing we can do.
|
|
140
|
+
nil
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def tag_for_latest_version
|
|
144
|
+
return unless git_commit_checker.git_dependency?
|
|
145
|
+
return unless git_commit_checker.pinned?
|
|
146
|
+
return unless git_commit_checker.pinned_ref_looks_like_version?
|
|
147
|
+
|
|
148
|
+
latest_tag = git_commit_checker.local_tag_for_latest_version&.
|
|
149
|
+
fetch(:tag)
|
|
150
|
+
|
|
151
|
+
version_rgx = GitCommitChecker::VERSION_REGEX
|
|
152
|
+
return unless latest_tag.match(version_rgx)
|
|
153
|
+
|
|
154
|
+
latest_tag
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def proxy_requirement?
|
|
158
|
+
dependency.requirements.any? do |req|
|
|
159
|
+
req.fetch(:source)&.fetch(:proxy_url, nil)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def registry_dependency?
|
|
164
|
+
return false if dependency_source_details.nil?
|
|
165
|
+
|
|
166
|
+
dependency_source_details.fetch(:type) == "registry"
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def dependency_source_details
|
|
170
|
+
sources =
|
|
171
|
+
dependency.requirements.map { |r| r.fetch(:source) }.uniq.compact
|
|
172
|
+
|
|
173
|
+
raise "Multiple sources! #{sources.join(', ')}" if sources.count > 1
|
|
174
|
+
|
|
175
|
+
sources.first
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def git_dependency?
|
|
179
|
+
git_commit_checker.git_dependency?
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def git_commit_checker
|
|
183
|
+
@git_commit_checker ||=
|
|
184
|
+
GitCommitChecker.new(
|
|
185
|
+
dependency: dependency,
|
|
186
|
+
credentials: credentials,
|
|
187
|
+
ignored_versions: ignored_versions,
|
|
188
|
+
requirement_class: Requirement,
|
|
189
|
+
version_class: Version
|
|
190
|
+
)
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
Dependabot::UpdateCheckers.
|
|
197
|
+
register("terraform", Dependabot::Terraform::UpdateChecker)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Terraform pre-release versions use 1.0.1-rc1 syntax, which Gem::Version
|
|
4
|
+
# converts into 1.0.1.pre.rc1. We override the `to_s` method to stop that
|
|
5
|
+
# alteration.
|
|
6
|
+
#
|
|
7
|
+
# See, for example, https://releases.hashicorp.com/terraform/
|
|
8
|
+
|
|
9
|
+
module Dependabot
|
|
10
|
+
module Terraform
|
|
11
|
+
class Version < Gem::Version
|
|
12
|
+
def initialize(version)
|
|
13
|
+
@version_string = version.to_s
|
|
14
|
+
super
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def to_s
|
|
18
|
+
@version_string
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
Dependabot::Utils.
|
|
25
|
+
register_version_class("terraform", Dependabot::Terraform::Version)
|
metadata
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: dependabot-terraform
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.76.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Dependabot
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2018-12-06 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: dependabot-core
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - '='
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 0.76.1
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - '='
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: 0.76.1
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: byebug
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '10.0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '10.0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: rake
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '12'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '12'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rspec
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '3.8'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '3.8'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: rspec-its
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '1.2'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '1.2'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: rspec_junit_formatter
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - "~>"
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0.4'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - "~>"
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '0.4'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: rubocop
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - "~>"
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '0.61'
|
|
104
|
+
type: :development
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - "~>"
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '0.61'
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: vcr
|
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - "~>"
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '4.0'
|
|
118
|
+
type: :development
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - "~>"
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: '4.0'
|
|
125
|
+
- !ruby/object:Gem::Dependency
|
|
126
|
+
name: webmock
|
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
|
128
|
+
requirements:
|
|
129
|
+
- - "~>"
|
|
130
|
+
- !ruby/object:Gem::Version
|
|
131
|
+
version: '3.4'
|
|
132
|
+
type: :development
|
|
133
|
+
prerelease: false
|
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
135
|
+
requirements:
|
|
136
|
+
- - "~>"
|
|
137
|
+
- !ruby/object:Gem::Version
|
|
138
|
+
version: '3.4'
|
|
139
|
+
description: Automated dependency management for Ruby, JavaScript, Python, PHP, Elixir,
|
|
140
|
+
Rust, Java, .NET, Elm and Go
|
|
141
|
+
email: support@dependabot.com
|
|
142
|
+
executables: []
|
|
143
|
+
extensions: []
|
|
144
|
+
extra_rdoc_files: []
|
|
145
|
+
files:
|
|
146
|
+
- helpers/build
|
|
147
|
+
- lib/dependabot/terraform.rb
|
|
148
|
+
- lib/dependabot/terraform/file_fetcher.rb
|
|
149
|
+
- lib/dependabot/terraform/file_parser.rb
|
|
150
|
+
- lib/dependabot/terraform/file_updater.rb
|
|
151
|
+
- lib/dependabot/terraform/metadata_finder.rb
|
|
152
|
+
- lib/dependabot/terraform/requirement.rb
|
|
153
|
+
- lib/dependabot/terraform/requirements_updater.rb
|
|
154
|
+
- lib/dependabot/terraform/update_checker.rb
|
|
155
|
+
- lib/dependabot/terraform/version.rb
|
|
156
|
+
homepage: https://github.com/dependabot/dependabot-core
|
|
157
|
+
licenses:
|
|
158
|
+
- Nonstandard
|
|
159
|
+
metadata: {}
|
|
160
|
+
post_install_message:
|
|
161
|
+
rdoc_options: []
|
|
162
|
+
require_paths:
|
|
163
|
+
- lib
|
|
164
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
165
|
+
requirements:
|
|
166
|
+
- - ">="
|
|
167
|
+
- !ruby/object:Gem::Version
|
|
168
|
+
version: 2.5.0
|
|
169
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
170
|
+
requirements:
|
|
171
|
+
- - ">="
|
|
172
|
+
- !ruby/object:Gem::Version
|
|
173
|
+
version: 2.5.0
|
|
174
|
+
requirements: []
|
|
175
|
+
rubyforge_project:
|
|
176
|
+
rubygems_version: 2.7.6
|
|
177
|
+
signing_key:
|
|
178
|
+
specification_version: 4
|
|
179
|
+
summary: Terraform support for dependabot-core
|
|
180
|
+
test_files: []
|