dependabot-terraform 0.147.1 → 0.148.0

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: 1513bb43b2a242c080012555ab17869d6c062e941912c36dcc5ea76476be03b9
4
- data.tar.gz: 731c9a7cd5658b622f7a24e87ea9a48909094af9cba08deb9850cb55a7f19bd9
3
+ metadata.gz: 7d7706b0b1a4caf093c24a35a6c49398abfa8c96b6ad18dc7db44bab894c7581
4
+ data.tar.gz: f482a7a71f487f0815ff464b189edab66c0a77e3d4ffdd1b5a5080e17680de27
5
5
  SHA512:
6
- metadata.gz: 3ea9a436607ccb4b54b21743d6952d41b4a86bd344724372facdb6368b1fd6b1d24b7327ae569738280b8bd4187ae78f9fad0b458ac8832665a0741ca380eb33
7
- data.tar.gz: b438039b06970eaf03a187567432a785e4f7eb2e9f61b18dffbdec99aa39080e2c322467d40b830af9b83356355a1d4702f61bbe84c9bdac50187309e7709a4d
6
+ metadata.gz: bc1983b4f79f865bac2a1f12f379c96f649b7b4056d67c96a6a3d6d2ad0a27e653b216baad1f85aba931bd8c1510a01e4913e32307b91a4311b2160c60e80f44
7
+ data.tar.gz: d7b18e3cb48784d3fe22c305b6f609d75a7c553d3191305c3b30d2cecd45274a85b615f9513867f08a9d9ff07ae8b248dab1a8198a3c724b159c58016c0126f4
@@ -27,7 +27,16 @@ module Dependabot
27
27
  terraform_files.each do |file|
28
28
  modules = parsed_file(file).fetch("module", {})
29
29
  modules.each do |name, details|
30
- dependency_set << build_terraform_dependency(file, name, details)
30
+ dependency_set << build_terraform_dependency(file, name, details, false)
31
+ end
32
+
33
+ parsed_file(file).fetch("terraform", []).each do |terraform|
34
+ required_providers = terraform.fetch("required_providers", {})
35
+ required_providers.each do |provider|
36
+ provider.each do |name, details|
37
+ dependency_set << build_terraform_dependency(file, name, details, true)
38
+ end
39
+ end
31
40
  end
32
41
  end
33
42
 
@@ -45,12 +54,15 @@ module Dependabot
45
54
 
46
55
  private
47
56
 
48
- def build_terraform_dependency(file, name, details)
49
- details = details.first
57
+ def build_terraform_dependency(file, name, details, provider)
58
+ details = details.is_a?(Array) ? details.first : details
50
59
 
51
- source = source_from(details)
52
- dep_name =
53
- source[:type] == "registry" ? source[:module_identifier] : name
60
+ source = source_from(details, provider)
61
+ dep_name = case source[:type]
62
+ when "registry" then source[:module_identifier]
63
+ when "provider" then details["source"]
64
+ else name
65
+ end
54
66
  version_req = details["version"]&.strip
55
67
  version =
56
68
  if source[:type] == "git" then version_from_ref(source[:ref])
@@ -71,7 +83,7 @@ module Dependabot
71
83
  end
72
84
 
73
85
  def build_terragrunt_dependency(file, details)
74
- source = source_from(details)
86
+ source = source_from(details, false)
75
87
  dep_name =
76
88
  if Source.from_url(source[:url])
77
89
  Source.from_url(source[:url]).repo
@@ -95,7 +107,7 @@ module Dependabot
95
107
  end
96
108
 
97
109
  # Full docs at https://www.terraform.io/docs/modules/sources.html
98
- def source_from(details_hash)
110
+ def source_from(details_hash, provider)
99
111
  raw_source = details_hash.fetch("source")
100
112
  bare_source = get_proxied_source(raw_source)
101
113
 
@@ -106,17 +118,23 @@ module Dependabot
106
118
  when :github, :bitbucket, :git
107
119
  git_source_details_from(bare_source)
108
120
  when :registry
109
- registry_source_details_from(bare_source)
121
+ registry_source_details_from(bare_source, provider)
110
122
  end
111
123
 
112
124
  source_details[:proxy_url] = raw_source if raw_source != bare_source
113
125
  source_details
114
126
  end
115
127
 
116
- def registry_source_details_from(source_string)
128
+ def registry_source_details_from(source_string, provider)
117
129
  parts = source_string.split("//").first.split("/")
118
130
 
119
- if parts.count == 3
131
+ if provider && parts.count == 2
132
+ {
133
+ "type": "provider",
134
+ "registry_hostname": "registry.terraform.io",
135
+ "module_identifier": source_string
136
+ }
137
+ elsif parts.count == 3
120
138
  {
121
139
  type: "registry",
122
140
  registry_hostname: "registry.terraform.io",
@@ -47,7 +47,7 @@ module Dependabot
47
47
  case new_req[:source][:type]
48
48
  when "git"
49
49
  update_git_declaration(new_req, old_req, content, file.name)
50
- when "registry"
50
+ when "registry", "provider"
51
51
  update_registry_declaration(new_req, old_req, content)
52
52
  else
53
53
  raise "Don't know how to update a #{new_req[:source][:type]} "\
@@ -15,6 +15,7 @@ module Dependabot
15
15
  case new_source_type
16
16
  when "git" then find_source_from_git_url
17
17
  when "registry" then find_source_from_registry_details
18
+ when "provider" then find_source_from_provider_details
18
19
  else raise "Unexpected source type: #{new_source_type}"
19
20
  end
20
21
  end
@@ -36,29 +37,11 @@ module Dependabot
36
37
  Source.from_url(url)
37
38
  end
38
39
 
39
- # Registry API docs:
40
- # https://www.terraform.io/docs/registry/api.html
41
40
  def find_source_from_registry_details
42
41
  info = dependency.requirements.map { |r| r[:source] }.compact.first
43
-
44
42
  hostname = info[:registry_hostname] || info["registry_hostname"]
45
43
 
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
- raise "Response from registry was #{response.status}" unless response.status == 200
59
-
60
- source_url = JSON.parse(response.body).fetch("source")
61
- Source.from_url(source_url) if source_url
44
+ RegistryClient.new(hostname: hostname).source(dependency: dependency)
62
45
  end
63
46
  end
64
47
  end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dependabot/dependency"
4
+ require "dependabot/source"
5
+ require "dependabot/terraform/version"
6
+
7
+ module Dependabot
8
+ module Terraform
9
+ # Terraform::RegistryClient is a basic API client to interact with a
10
+ # terraform registry: https://www.terraform.io/docs/registry/api.html
11
+ class RegistryClient
12
+ PUBLIC_HOSTNAME = "registry.terraform.io"
13
+
14
+ def initialize(hostname:)
15
+ @hostname = hostname
16
+ end
17
+
18
+ # Fetch all the versions of a provider, and return a Version
19
+ # representation of them.
20
+ #
21
+ # @param identifier [String] the identifier for the dependency, i.e:
22
+ # "hashicorp/aws"
23
+ # @return [Array<Dependabot::Terraform::Version>]
24
+ # @raise [RuntimeError] when the versions cannot be retrieved
25
+ def all_provider_versions(identifier:)
26
+ # TODO: Implement service discovery for custom registries
27
+ return [] unless hostname == PUBLIC_HOSTNAME
28
+
29
+ response = get(endpoint: "providers/#{identifier}/versions")
30
+
31
+ JSON.parse(response).
32
+ fetch("versions").
33
+ map { |release| version_class.new(release.fetch("version")) }
34
+ end
35
+
36
+ # Fetch all the versions of a module, and return a Version
37
+ # representation of them.
38
+ #
39
+ # @param identifier [String] the identifier for the dependency, i.e:
40
+ # "hashicorp/consul/aws"
41
+ # @return [Array<Dependabot::Terraform::Version>]
42
+ # @raise [RuntimeError] when the versions cannot be retrieved
43
+ def all_module_versions(identifier:)
44
+ # TODO: Implement service discovery for custom registries
45
+ return [] unless hostname == PUBLIC_HOSTNAME
46
+
47
+ response = get(endpoint: "modules/#{identifier}/versions")
48
+
49
+ JSON.parse(response).
50
+ fetch("modules").first.fetch("versions").
51
+ map { |release| version_class.new(release.fetch("version")) }
52
+ end
53
+
54
+ # Fetch the "source" for a module or provider. We use the API to fetch
55
+ # the source for a dependency, this typically points to a source code
56
+ # repository, and then instantiate a Dependabot::Source object that we
57
+ # can use to fetch Metadata about a specific version of the dependency.
58
+ #
59
+ # @param dependency [Dependabot::Dependency] the dependency who's source
60
+ # we're attempting to find
61
+ # @return Dependabot::Source
62
+ # @raise [RuntimeError] when the source cannot be retrieved
63
+ def source(dependency:)
64
+ # TODO: Implement service discovery for custom registries
65
+ return unless hostname == PUBLIC_HOSTNAME
66
+
67
+ endpoint = if dependency.requirements.first[:source][:type] == "registry"
68
+ "modules/#{dependency.name}/#{dependency.version}"
69
+ elsif dependency.source[:type] == "provider"
70
+ "providers/#{dependency.name}/#{dependency.version}"
71
+ else
72
+ raise "Invalid source type"
73
+ end
74
+ response = get(endpoint: endpoint)
75
+
76
+ source_url = JSON.parse(response).fetch("source")
77
+ Source.from_url(source_url) if source_url
78
+ end
79
+
80
+ private
81
+
82
+ attr_reader :hostname
83
+
84
+ def get(endpoint:)
85
+ url = "https://#{hostname}/v1/#{endpoint}"
86
+
87
+ response = Excon.get(
88
+ url,
89
+ idempotent: true,
90
+ **SharedHelpers.excon_defaults
91
+ )
92
+
93
+ raise "Response from registry was #{response.status}" unless response.status == 200
94
+
95
+ response.body
96
+ end
97
+
98
+ def version_class
99
+ Version
100
+ end
101
+ end
102
+ end
103
+ end
@@ -72,7 +72,7 @@ module Dependabot
72
72
  requirements.map do |req|
73
73
  case req.dig(:source, :type)
74
74
  when "git" then update_git_requirement(req)
75
- when "registry" then update_registry_requirement(req)
75
+ when "registry", "provider" then update_registry_requirement(req)
76
76
  else req
77
77
  end
78
78
  end
@@ -6,6 +6,7 @@ require "dependabot/git_commit_checker"
6
6
  require "dependabot/terraform/requirements_updater"
7
7
  require "dependabot/terraform/requirement"
8
8
  require "dependabot/terraform/version"
9
+ require "dependabot/terraform/registry_client"
9
10
 
10
11
  module Dependabot
11
12
  module Terraform
@@ -13,6 +14,7 @@ module Dependabot
13
14
  def latest_version
14
15
  return latest_version_for_git_dependency if git_dependency?
15
16
  return latest_version_for_registry_dependency if registry_dependency?
17
+ return latest_version_for_provider_dependency if provider_dependency?
16
18
  # Other sources (mercurial, path dependencies) just return `nil`
17
19
  end
18
20
 
@@ -65,34 +67,40 @@ module Dependabot
65
67
 
66
68
  return @latest_version_for_registry_dependency if @latest_version_for_registry_dependency
67
69
 
68
- versions = all_registry_versions
70
+ versions = all_module_versions
69
71
  versions.reject!(&:prerelease?) unless wants_prerelease?
70
72
  versions.reject! { |v| ignore_requirements.any? { |r| r.satisfied_by?(v) } }
71
73
 
72
74
  @latest_version_for_registry_dependency = versions.max
73
75
  end
74
76
 
75
- def all_registry_versions
76
- hostname = dependency_source_details.fetch(:registry_hostname)
77
+ def all_module_versions
77
78
  identifier = dependency_source_details.fetch(:module_identifier)
79
+ registry_client.all_module_versions(identifier: identifier)
80
+ end
81
+
82
+ def all_provider_versions
83
+ identifier = dependency_source_details.fetch(:module_identifier)
84
+ registry_client.all_provider_versions(identifier: identifier)
85
+ end
78
86
 
79
- # TODO: Implement service discovery for custom registries
80
- return [] unless hostname == "registry.terraform.io"
87
+ def registry_client
88
+ @registry_client ||= begin
89
+ hostname = dependency_source_details.fetch(:registry_hostname)
90
+ RegistryClient.new(hostname: hostname)
91
+ end
92
+ end
81
93
 
82
- url = "https://registry.terraform.io/v1/modules/"\
83
- "#{identifier}/versions"
94
+ def latest_version_for_provider_dependency
95
+ return unless provider_dependency?
84
96
 
85
- response = Excon.get(
86
- url,
87
- idempotent: true,
88
- **SharedHelpers.excon_defaults
89
- )
97
+ return @latest_version_for_provider_dependency if @latest_version_for_provider_dependency
90
98
 
91
- raise "Response from registry was #{response.status}" unless response.status == 200
99
+ versions = all_provider_versions
100
+ versions.reject!(&:prerelease?) unless wants_prerelease?
101
+ versions.reject! { |v| ignore_requirements.any? { |r| r.satisfied_by?(v) } }
92
102
 
93
- JSON.parse(response.body).
94
- fetch("modules").first.fetch("versions").
95
- map { |release| version_class.new(release.fetch("version")) }
103
+ @latest_version_for_provider_dependency = versions.max
96
104
  end
97
105
 
98
106
  def wants_prerelease?
@@ -160,6 +168,12 @@ module Dependabot
160
168
  dependency_source_details.fetch(:type) == "registry"
161
169
  end
162
170
 
171
+ def provider_dependency?
172
+ return false if dependency_source_details.nil?
173
+
174
+ dependency_source_details.fetch(:type) == "provider"
175
+ end
176
+
163
177
  def dependency_source_details
164
178
  sources =
165
179
  dependency.requirements.map { |r| r.fetch(:source) }.uniq.compact
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dependabot-terraform
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.147.1
4
+ version: 0.148.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 0.147.1
19
+ version: 0.148.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 0.147.1
26
+ version: 0.148.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: byebug
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -192,6 +192,7 @@ files:
192
192
  - lib/dependabot/terraform/file_selector.rb
193
193
  - lib/dependabot/terraform/file_updater.rb
194
194
  - lib/dependabot/terraform/metadata_finder.rb
195
+ - lib/dependabot/terraform/registry_client.rb
195
196
  - lib/dependabot/terraform/requirement.rb
196
197
  - lib/dependabot/terraform/requirements_updater.rb
197
198
  - lib/dependabot/terraform/update_checker.rb