dependabot-hex 0.213.0 → 0.214.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: 30d16ac35f7e452a754f78329c9ef9ba1fd65758264be8220338a721e66dbcb6
4
- data.tar.gz: 94a09051bf06589fe462aa33ab4440d1c5207069a23d8d45aea8517aeb16427e
3
+ metadata.gz: 66e2212df8486009a3da3b280dc0c36e342556e4765760c0c3dc38a71e034a00
4
+ data.tar.gz: d3a62fc111e0e50212cb617bc19f8e86077bf681b3b1d82d1950e16fc283a67a
5
5
  SHA512:
6
- metadata.gz: b9461aa63f1f9a7f6184b29659bbf2a006642fb70eb402787ecbe9a747251c6ae2bf0dd6b484f4e80fa9a0b4b5db42fc2d9eb838aa84c8277a261ce8c03113c8
7
- data.tar.gz: 4a9453c6d17e8af0037c637041726c0c5e12fdcb9e061eaf208051fecf644ac6a54b2d6dcfbed7ed423b6593f6d7ad1c331a1cba796a113c89c0f4570ceb16c4
6
+ metadata.gz: 9233d16f7d684b4e79ed18914d2512c7c2f72d9ae5a6b23d86298f7ac01088d0b43caac63367339974337d36b448ff037906c49867bc1a4f12ebf5a65c052bfa
7
+ data.tar.gz: 564a484640d7135729f86dbe931debabda0c14255c5098a4e1041ec97b2f8dfe69f4fe86ba5bf1bea86223037a7e335de6621feec39e6429ca464549bd76e591
data/helpers/build CHANGED
@@ -10,7 +10,14 @@ fi
10
10
  install_dir="$DEPENDABOT_NATIVE_HELPERS_PATH/hex"
11
11
  mkdir -p "$install_dir"
12
12
 
13
- mix local.hex --force
13
+ # Initial Hex install - will always be the latest available version
14
+ mix local.hex --force --if-missing
15
+ # Annoyingly, a specific Hex version cannot be specified during the initial install.
16
+ # The only way to pin is to re-install.
17
+ if [ -n "$HEX_VERSION" ]; then
18
+ mix hex.install "$HEX_VERSION"
19
+ fi
20
+
14
21
  mix archive.install hex nerves_bootstrap --force
15
22
 
16
23
  helpers_dir="$(dirname "${BASH_SOURCE[0]}")"
@@ -1,9 +1,8 @@
1
1
  defmodule UpdateChecker do
2
- def run(dependency_name, credentials) do
3
- set_credentials(credentials)
4
-
2
+ def run(dependency_name) do
5
3
  # Update the lockfile in a session that we can time out
6
4
  task = Task.async(fn -> do_resolution(dependency_name) end)
5
+
7
6
  case Task.yield(task, 30000) || Task.shutdown(task) do
8
7
  {:ok, {:ok, :resolution_successful}} ->
9
8
  # Read the new lock
@@ -15,43 +14,20 @@ defmodule UpdateChecker do
15
14
  updated_lock
16
15
  |> Map.get(String.to_atom(dependency_name))
17
16
  |> elem(2)
17
+
18
18
  {:ok, version}
19
19
 
20
- {:ok, {:error, error}} -> {:error, error}
20
+ {:ok, {:error, error}} ->
21
+ {:error, error}
21
22
 
22
- nil -> {:error, :dependency_resolution_timed_out}
23
+ nil ->
24
+ {:error, :dependency_resolution_timed_out}
23
25
 
24
- {:exit, reason} -> {:error, reason}
26
+ {:exit, reason} ->
27
+ {:error, reason}
25
28
  end
26
29
  end
27
30
 
28
- defp set_credentials(credentials) do
29
- credentials
30
- |> Enum.reduce([], fn cred, acc ->
31
- if List.last(acc) == nil || List.last(acc)[:token] do
32
- List.insert_at(acc, -1, %{organization: cred})
33
- else
34
- {item, acc} = List.pop_at(acc, -1)
35
- item = Map.put(item, :token, cred)
36
- List.insert_at(acc, -1, item)
37
- end
38
- end)
39
- |> Enum.each(fn cred ->
40
- hexpm = Hex.Repo.get_repo("hexpm")
41
-
42
- repo = %{
43
- url: hexpm.url <> "/repos/#{cred.organization}",
44
- public_key: nil,
45
- auth_key: cred.token
46
- }
47
-
48
- Hex.Config.read()
49
- |> Hex.Config.read_repos()
50
- |> Map.put("hexpm:#{cred.organization}", repo)
51
- |> Hex.Config.update_repos()
52
- end)
53
- end
54
-
55
31
  defp do_resolution(dependency_name) do
56
32
  # Fetch dependencies that needs updating
57
33
  {dependency_lock, rest_lock} =
@@ -59,6 +35,7 @@ defmodule UpdateChecker do
59
35
 
60
36
  try do
61
37
  Mix.Dep.Fetcher.by_name([dependency_name], dependency_lock, rest_lock, [])
38
+
62
39
  {:ok, :resolution_successful}
63
40
  rescue
64
41
  error -> {:error, error}
@@ -66,15 +43,14 @@ defmodule UpdateChecker do
66
43
  end
67
44
  end
68
45
 
69
- [dependency_name | credentials] = System.argv()
70
-
46
+ [dependency_name] = System.argv()
71
47
 
72
- case UpdateChecker.run(dependency_name, credentials) do
48
+ case UpdateChecker.run(dependency_name) do
73
49
  {:ok, version} ->
74
50
  version = :erlang.term_to_binary({:ok, version})
75
51
  IO.write(:stdio, version)
76
52
 
77
- {:error, %Hex.Version.InvalidRequirementError{} = error} ->
53
+ {:error, %Version.InvalidRequirementError{} = error} ->
78
54
  result = :erlang.term_to_binary({:error, "Invalid requirement: #{error.requirement}"})
79
55
  IO.write(:stdio, result)
80
56
 
@@ -1,35 +1,11 @@
1
- [dependency_name | credentials] = System.argv()
2
-
3
- grouped_creds = Enum.reduce credentials, [], fn cred, acc ->
4
- if List.last(acc) == nil || List.last(acc)[:token] do
5
- List.insert_at(acc, -1, %{ organization: cred })
6
- else
7
- { item, acc } = List.pop_at(acc, -1)
8
- item = Map.put(item, :token, cred)
9
- List.insert_at(acc, -1, item)
10
- end
11
- end
12
-
13
- Enum.each grouped_creds, fn cred ->
14
- hexpm = Hex.Repo.get_repo("hexpm")
15
- repo = %{
16
- url: hexpm.url <> "/repos/#{cred.organization}",
17
- public_key: nil,
18
- auth_key: cred.token
19
- }
20
-
21
- Hex.Config.read()
22
- |> Hex.Config.read_repos()
23
- |> Map.put("hexpm:#{cred.organization}", repo)
24
- |> Hex.Config.update_repos()
25
- end
26
-
27
- # dependency atom
28
- dependency = String.to_atom(dependency_name)
1
+ dependency =
2
+ System.argv()
3
+ |> List.first()
4
+ |> String.to_atom()
29
5
 
30
6
  # Fetch dependencies that needs updating
31
7
  {dependency_lock, rest_lock} = Map.split(Mix.Dep.Lock.read(), [dependency])
32
- Mix.Dep.Fetcher.by_name([dependency_name], dependency_lock, rest_lock, [])
8
+ Mix.Dep.Fetcher.by_name([dependency], dependency_lock, rest_lock, [])
33
9
 
34
10
  System.cmd(
35
11
  "mix",
data/helpers/lib/run.exs CHANGED
@@ -11,7 +11,8 @@ defmodule DependencyHelper do
11
11
  {:ok, :erlang.binary_to_term(output)}
12
12
  end
13
13
 
14
- {error, 1} -> {:error, error}
14
+ {error, 1} ->
15
+ {:error, error}
15
16
  end
16
17
  |> handle_result()
17
18
  end
@@ -40,37 +41,115 @@ defmodule DependencyHelper do
40
41
  run_script("parse_deps.exs", dir)
41
42
  end
42
43
 
43
- defp run(%{"function" => "get_latest_resolvable_version", "args" => [dir, dependency_name, credentials]}) do
44
- run_script("check_update.exs", dir, [dependency_name] ++ credentials)
44
+ defp run(%{
45
+ "function" => "get_latest_resolvable_version",
46
+ "args" => [dir, dependency_name, credentials]
47
+ }) do
48
+ set_credentials(credentials)
49
+
50
+ run_script("check_update.exs", dir, [dependency_name])
45
51
  end
46
52
 
47
53
  defp run(%{"function" => "get_updated_lockfile", "args" => [dir, dependency_name, credentials]}) do
48
- run_script("do_update.exs", dir, [dependency_name] ++ credentials)
54
+ set_credentials(credentials)
55
+
56
+ run_script("do_update.exs", dir, [dependency_name])
49
57
  end
50
58
 
51
59
  defp run_script(script, dir, args \\ []) do
52
- args = [
53
- "run",
54
- "--no-deps-check",
55
- "--no-start",
56
- "--no-compile",
57
- "--no-elixir-version-check",
58
- script
59
- ] ++ args
60
+ args =
61
+ [
62
+ "run",
63
+ "--no-deps-check",
64
+ "--no-start",
65
+ "--no-compile",
66
+ "--no-elixir-version-check",
67
+ script
68
+ ] ++ args
60
69
 
61
70
  System.cmd(
62
71
  "mix",
63
72
  args,
64
- [
65
- cd: dir,
66
- env: %{
67
- "MIX_EXS" => nil,
68
- "MIX_LOCK" => nil,
69
- "MIX_DEPS" => nil
70
- }
71
- ]
73
+ cd: dir,
74
+ env: %{
75
+ "MIX_EXS" => nil,
76
+ "MIX_LOCK" => nil,
77
+ "MIX_DEPS" => nil
78
+ }
72
79
  )
73
80
  end
81
+
82
+ defp set_credentials([]), do: :ok
83
+
84
+ defp set_credentials(["hex_organization", organization, token | tail]) do
85
+ url =
86
+ "hexpm"
87
+ |> Hex.Repo.get_repo()
88
+ |> Map.fetch!(:url)
89
+ |> URI.merge("/repos/#{organization}")
90
+ |> to_string()
91
+
92
+ update_repos("hexpm:#{organization}", %{url: url, public_key: nil, auth_key: token})
93
+
94
+ set_credentials(tail)
95
+ end
96
+
97
+ defp set_credentials(["hex_repository", repo, url, auth_key, fingerprint | tail]) do
98
+ case fetch_public_key(repo, url, auth_key, fingerprint) do
99
+ {:ok, public_key} ->
100
+ update_repos(repo, %{auth_key: auth_key, public_key: public_key, url: url})
101
+
102
+ set_credentials(tail)
103
+
104
+ error ->
105
+ handle_result(error)
106
+ end
107
+ end
108
+
109
+ defp set_credentials([_mode, org_or_url | _]) do
110
+ handle_result({:error, "Missing credentials for \"#{org_or_url}\""})
111
+ end
112
+
113
+ defp update_repos(name, opts) do
114
+ Hex.Config.read()
115
+ |> Hex.Config.read_repos()
116
+ |> Map.put(name, opts)
117
+ |> Hex.Config.update_repos()
118
+ end
119
+
120
+ defp fetch_public_key(repo, repo_url, auth_key, fingerprint) do
121
+ case Hex.Repo.get_public_key(repo_url, auth_key) do
122
+ {:ok, {200, key, _}} ->
123
+ if public_key_matches?(key, fingerprint) do
124
+ {:ok, key}
125
+ else
126
+ {:error, "Public key fingerprint mismatch for repo \"#{repo}\""}
127
+ end
128
+
129
+ {:ok, {code, _, _}} ->
130
+ {:error, "Downloading public key for repo \"#{repo}\" failed with code: #{inspect(code)}"}
131
+
132
+ other ->
133
+ {:error, "Downloading public key for repo \"#{repo}\" failed: #{inspect(other)}"}
134
+ end
135
+ end
136
+
137
+ defp public_key_matches?(_public_key, _fingerprint = ""), do: true
138
+
139
+ defp public_key_matches?(public_key, fingerprint) do
140
+ public_key =
141
+ public_key
142
+ |> :public_key.pem_decode()
143
+ |> List.first()
144
+ |> :public_key.pem_entry_decode()
145
+
146
+ decoded_fingerprint =
147
+ :sha256
148
+ |> :ssh.hostkey_fingerprint(public_key)
149
+ |> List.to_string()
150
+
151
+ decoded_fingerprint == fingerprint
152
+ end
74
153
  end
75
154
 
76
155
  DependencyHelper.main()
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dependabot
4
+ module Hex
5
+ module CredentialHelpers
6
+ def self.hex_credentials(credentials)
7
+ organization_credentials(credentials) + repo_credentials(credentials)
8
+ end
9
+
10
+ def self.organization_credentials(credentials)
11
+ defaults = { "organization" => "", "token" => "" }
12
+ keys = %w(type organization token)
13
+
14
+ credentials.
15
+ select { |cred| cred["type"] == "hex_organization" }.
16
+ flat_map { |cred| defaults.merge(cred).slice(*keys).values }
17
+ end
18
+
19
+ def self.repo_credentials(credentials)
20
+ # Credentials are serialized as an array that may not have optional fields. Using a
21
+ # default ensures that the array is always the same length, even if values are empty.
22
+ defaults = { "url" => "", "auth_key" => "", "public_key_fingerprint" => "" }
23
+ keys = %w(type repo url auth_key public_key_fingerprint)
24
+
25
+ credentials.
26
+ select { |cred| cred["type"] == "hex_repository" }.
27
+ flat_map { |cred| defaults.merge(cred).slice(*keys).values }
28
+ end
29
+ end
30
+ end
31
+ end
@@ -4,8 +4,9 @@ require "dependabot/hex/file_updater"
4
4
  require "dependabot/hex/file_updater/mixfile_updater"
5
5
  require "dependabot/hex/file_updater/mixfile_sanitizer"
6
6
  require "dependabot/hex/file_updater/mixfile_requirement_updater"
7
- require "dependabot/hex/version"
7
+ require "dependabot/hex/credential_helpers"
8
8
  require "dependabot/hex/native_helpers"
9
+ require "dependabot/hex/version"
9
10
  require "dependabot/shared_helpers"
10
11
 
11
12
  module Dependabot
@@ -29,7 +30,7 @@ module Dependabot
29
30
  env: mix_env,
30
31
  command: "mix run #{elixir_helper_path}",
31
32
  function: "get_updated_lockfile",
32
- args: [Dir.pwd, dependency.name, organization_credentials]
33
+ args: [Dir.pwd, dependency.name, CredentialHelpers.hex_credentials(credentials)]
33
34
  )
34
35
  end
35
36
  end
@@ -131,11 +132,6 @@ module Dependabot
131
132
  def lockfile
132
133
  @lockfile ||= dependency_files.find { |f| f.name == "mix.lock" }
133
134
  end
134
-
135
- def organization_credentials
136
- credentials.select { |cred| cred["type"] == "hex_organization" }.
137
- flat_map { |cred| [cred["organization"], cred.fetch("token", "")] }
138
- end
139
135
  end
140
136
  end
141
137
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "dependabot/hex/version"
4
4
  require "dependabot/hex/update_checker"
5
+ require "dependabot/hex/credential_helpers"
5
6
  require "dependabot/hex/native_helpers"
6
7
  require "dependabot/hex/file_updater/mixfile_sanitizer"
7
8
  require "dependabot/shared_helpers"
@@ -32,10 +33,7 @@ module Dependabot
32
33
  latest_resolvable_version =
33
34
  SharedHelpers.in_a_temporary_directory do
34
35
  write_temporary_sanitized_dependency_files
35
- FileUtils.cp(
36
- elixir_helper_check_update_path,
37
- "check_update.exs"
38
- )
36
+ FileUtils.cp(elixir_helper_check_update_path, "check_update.exs")
39
37
 
40
38
  SharedHelpers.with_git_configured(credentials: credentials) do
41
39
  run_elixir_update_checker
@@ -55,23 +53,31 @@ module Dependabot
55
53
  env: mix_env,
56
54
  command: "mix run #{elixir_helper_path}",
57
55
  function: "get_latest_resolvable_version",
58
- args: [Dir.pwd,
59
- dependency.name,
60
- organization_credentials],
56
+ args: [Dir.pwd, dependency.name, CredentialHelpers.hex_credentials(credentials)],
61
57
  stderr_to_stdout: true
62
58
  )
63
59
  end
64
60
 
65
61
  def handle_hex_errors(error)
66
- if error.message.include?("No authenticated organization found")
67
- org = error.message.match(/found for ([a-z_]+)\./).captures.first
68
- raise Dependabot::PrivateSourceAuthenticationFailure, org
62
+ if (match = error.message.match(/No authenticated organization found for (?<repo>[a-z_]+)\./))
63
+ raise Dependabot::PrivateSourceAuthenticationFailure, match[:repo]
69
64
  end
70
65
 
71
- if error.message.include?("Failed to fetch record for")
72
- org_match = error.message.match(%r{for 'hexpm:([a-z_]+)/})
73
- org = org_match&.captures&.first
74
- raise Dependabot::PrivateSourceAuthenticationFailure, org if org
66
+ if (match = error.message.match(/Public key fingerprint mismatch for repo "(?<repo>[a-z_]+)"/))
67
+ raise Dependabot::PrivateSourceAuthenticationFailure, match[:repo]
68
+ end
69
+
70
+ if (match = error.message.match(/Missing credentials for "(?<repo>[a-z_]+)"/))
71
+ raise Dependabot::PrivateSourceAuthenticationFailure, match[:repo]
72
+ end
73
+
74
+ if (match = error.message.match(/Downloading public key for repo "(?<repo>[a-z_]+)"/))
75
+ raise Dependabot::PrivateSourceAuthenticationFailure, match[:repo]
76
+ end
77
+
78
+ if (match = error.message.match(/Failed to fetch record for '(?<repo>[a-z_]+)(?::(?<org>[a-z_]+))?/))
79
+ name = match[:org] || match[:repo]
80
+ raise Dependabot::PrivateSourceAuthenticationFailure, name
75
81
  end
76
82
 
77
83
  # TODO: Catch the warnings as part of the Elixir module. This happens
@@ -171,12 +177,6 @@ module Dependabot
171
177
  def elixir_helper_check_update_path
172
178
  File.join(NativeHelpers.hex_helpers_dir, "lib/check_update.exs")
173
179
  end
174
-
175
- def organization_credentials
176
- credentials.
177
- select { |cred| cred["type"] == "hex_organization" }.
178
- flat_map { |cred| [cred["organization"], cred.fetch("token", "")] }
179
- end
180
180
  end
181
181
  end
182
182
  end
@@ -231,7 +231,7 @@ module Dependabot
231
231
  # rubocop:enable Metrics/PerceivedComplexity
232
232
 
233
233
  def filter_lower_versions(versions_array)
234
- return versions_array unless current_version && version_class.correct?(current_version)
234
+ return versions_array unless current_version
235
235
 
236
236
  versions_array.select do |version|
237
237
  version > current_version
@@ -251,12 +251,6 @@ module Dependabot
251
251
  nil
252
252
  end
253
253
 
254
- def current_version
255
- return unless dependency.version && version_class.correct?(dependency.version)
256
-
257
- version_class.new(dependency.version)
258
- end
259
-
260
254
  def wants_prerelease?
261
255
  return true if current_version&.prerelease?
262
256
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dependabot-hex
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.213.0
4
+ version: 0.214.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-10-31 00:00:00.000000000 Z
11
+ date: 2022-12-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dependabot-common
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 0.213.0
19
+ version: 0.214.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.213.0
26
+ version: 0.214.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: debug
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 3.13.0
61
+ version: 4.0.0
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 3.13.0
68
+ version: 4.0.0
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rake
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -114,14 +114,14 @@ dependencies:
114
114
  requirements:
115
115
  - - "~>"
116
116
  - !ruby/object:Gem::Version
117
- version: 1.37.1
117
+ version: 1.39.0
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
- version: 1.37.1
124
+ version: 1.39.0
125
125
  - !ruby/object:Gem::Dependency
126
126
  name: rubocop-performance
127
127
  requirement: !ruby/object:Gem::Requirement
@@ -221,6 +221,7 @@ files:
221
221
  - helpers/mix.exs
222
222
  - helpers/mix.lock
223
223
  - lib/dependabot/hex.rb
224
+ - lib/dependabot/hex/credential_helpers.rb
224
225
  - lib/dependabot/hex/file_fetcher.rb
225
226
  - lib/dependabot/hex/file_parser.rb
226
227
  - lib/dependabot/hex/file_updater.rb