dependabot-pre_commit 0.363.0 → 0.364.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 992ba7269128211c8ce88fa7074245571db4980d9f2f9cee55b9dc90cbcc8c72
4
- data.tar.gz: 8879531e1850590a92fc0966ff189a8578c161460510eb6ab3988d3c7f3f7158
3
+ metadata.gz: d1bd3f81f06b4e86d4139cf07b731129092edf1f86f4febc799cefb7d67fd03e
4
+ data.tar.gz: 1fa2859dd9dafc4b09ab7718de4b59d2cff583c5991c5439644b40fb2c4a88c8
5
5
  SHA512:
6
- metadata.gz: 5cafdaa259570be35235bc3f15fa47cd04a9adf85871f726b36043a6d5afb4388154c1d8819db13a705dd63d84fba4a196f4a4541d5357f8360f0596e39d3b6a
7
- data.tar.gz: d073171bc7aecd0fe94f661a06be98d340ad454d7ae6a66cec9567ce00f39d4c4187ca422d28004a453630b5d6f64173f97ed04e915c4dcebfd28e8d9688f510
6
+ metadata.gz: e3c7c74f6e6565ca5ecf32734d4a9d7af200e8acaabec1856abb839815b5d26360375c915104a7b4c484abf0333e68685029ad5b793ab43da0179893f5088507
7
+ data.tar.gz: e1f3f69285a2cf37e1072ae5216693f51d764c48ef2de33dda2d669943900e41f085e4ec6a8529f761a11fedb58a2c82867a2ef9ab67fdb2b80c47942931203e
@@ -25,13 +25,6 @@ module Dependabot
25
25
 
26
26
  sig { override.returns(T::Array[DependencyFile]) }
27
27
  def fetch_files
28
- unless allow_beta_ecosystems?
29
- raise Dependabot::DependencyFileNotFound.new(
30
- nil,
31
- "PreCommit support is currently in beta. Set ALLOW_BETA_ECOSYSTEMS=true to enable it."
32
- )
33
- end
34
-
35
28
  fetched_files = []
36
29
  fetched_files << pre_commit_config
37
30
 
@@ -0,0 +1,181 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "yaml"
5
+ require "base64"
6
+ require "sorbet-runtime"
7
+ require "dependabot/clients/github_with_retries"
8
+ require "dependabot/shared_helpers"
9
+ require "dependabot/source"
10
+
11
+ require "dependabot/pre_commit/file_parser"
12
+
13
+ module Dependabot
14
+ module PreCommit
15
+ class FileParser < Dependabot::FileParsers::Base
16
+ # Fetches hook language information from the source repository's
17
+ # .pre-commit-hooks.yaml file. This is needed because the language field
18
+ # is typically defined in the hook repo, not in the consumer's
19
+ # .pre-commit-config.yaml file.
20
+ class HookLanguageFetcher
21
+ extend T::Sig
22
+
23
+ HOOKS_FILE = ".pre-commit-hooks.yaml"
24
+
25
+ sig do
26
+ params(
27
+ credentials: T::Array[Dependabot::Credential]
28
+ ).void
29
+ end
30
+ def initialize(credentials:)
31
+ @credentials = credentials
32
+ @hooks_cache = T.let({}, T::Hash[String, T.nilable(T::Array[T::Hash[String, T.untyped]])])
33
+ end
34
+
35
+ # Fetches the language for a specific hook from the hook source repository.
36
+ #
37
+ # @param repo_url [String] The URL of the hook repository (e.g., "https://github.com/psf/black")
38
+ # @param revision [String] The revision (tag, SHA, branch) to fetch from
39
+ # @param hook_id [String] The hook ID to look up (e.g., "black")
40
+ # @return [String, nil] The language for the hook, or nil if not found
41
+ sig do
42
+ params(
43
+ repo_url: String,
44
+ revision: String,
45
+ hook_id: String
46
+ ).returns(T.nilable(String))
47
+ end
48
+ def fetch_language(repo_url:, revision:, hook_id:)
49
+ hooks = fetch_hooks_from_repo(repo_url, revision)
50
+ return nil unless hooks
51
+
52
+ hook = hooks.find { |h| h["id"] == hook_id }
53
+ return nil unless hook
54
+
55
+ T.cast(hook["language"], T.nilable(String))
56
+ end
57
+
58
+ private
59
+
60
+ sig { returns(T::Array[Dependabot::Credential]) }
61
+ attr_reader :credentials
62
+
63
+ sig do
64
+ params(
65
+ repo_url: String,
66
+ revision: String
67
+ ).returns(T.nilable(T::Array[T::Hash[String, T.untyped]]))
68
+ end
69
+ def fetch_hooks_from_repo(repo_url, revision)
70
+ cache_key = "#{repo_url}@#{revision}"
71
+ return @hooks_cache[cache_key] if @hooks_cache.key?(cache_key)
72
+
73
+ hooks = fetch_hooks_internal(repo_url, revision)
74
+ @hooks_cache[cache_key] = hooks
75
+ hooks
76
+ end
77
+
78
+ sig do
79
+ params(
80
+ repo_url: String,
81
+ revision: String
82
+ ).returns(T.nilable(T::Array[T::Hash[String, T.untyped]]))
83
+ end
84
+ def fetch_hooks_internal(repo_url, revision)
85
+ source = Source.from_url(repo_url)
86
+ return fetch_via_git_clone(repo_url, revision) unless source
87
+ return fetch_via_git_clone(repo_url, revision) unless source.provider == "github"
88
+
89
+ fetch_from_github(source, revision)
90
+ rescue StandardError => e
91
+ Dependabot.logger.debug("Failed to fetch hooks from #{repo_url}@#{revision}: #{e.message}")
92
+ nil
93
+ end
94
+
95
+ sig do
96
+ params(
97
+ source: Dependabot::Source,
98
+ revision: String
99
+ ).returns(T.nilable(T::Array[T::Hash[String, T.untyped]]))
100
+ end
101
+ def fetch_from_github(source, revision)
102
+ response = github_client.send(
103
+ :contents,
104
+ source.repo,
105
+ path: HOOKS_FILE,
106
+ ref: revision
107
+ )
108
+ return nil unless response
109
+
110
+ content = Base64.decode64(response.content)
111
+ parse_hooks_yaml(content)
112
+ rescue Octokit::NotFound
113
+ Dependabot.logger.debug("#{HOOKS_FILE} not found in #{source.repo}@#{revision}")
114
+ nil
115
+ rescue StandardError => e
116
+ Dependabot.logger.debug("Error fetching from GitHub: #{e.message}")
117
+ fetch_via_git_clone("https://github.com/#{source.repo}", revision)
118
+ end
119
+
120
+ sig do
121
+ params(
122
+ repo_url: String,
123
+ revision: String
124
+ ).returns(T.nilable(T::Array[T::Hash[String, T.untyped]]))
125
+ end
126
+ def fetch_via_git_clone(repo_url, revision)
127
+ source = Source.from_url(repo_url)
128
+ return nil unless source
129
+
130
+ SharedHelpers.in_a_temporary_directory(File.dirname(source.repo)) do |temp_dir|
131
+ repo_contents_path = File.join(temp_dir, File.basename(source.repo))
132
+
133
+ SharedHelpers.run_shell_command(
134
+ "git clone --no-checkout --depth 1 #{repo_url} #{repo_contents_path}",
135
+ fingerprint: "git clone --no-checkout --depth 1 <url> <path>"
136
+ )
137
+
138
+ Dir.chdir(repo_contents_path) do
139
+ # Fetch the specific revision and checkout the hooks file
140
+ SharedHelpers.run_shell_command(
141
+ "git fetch --depth 1 origin #{revision}",
142
+ fingerprint: "git fetch --depth 1 origin <revision>"
143
+ )
144
+ SharedHelpers.run_shell_command(
145
+ "git checkout FETCH_HEAD -- #{HOOKS_FILE}",
146
+ fingerprint: "git checkout FETCH_HEAD -- <file>"
147
+ )
148
+
149
+ return nil unless File.exist?(HOOKS_FILE)
150
+
151
+ content = File.read(HOOKS_FILE)
152
+ parse_hooks_yaml(content)
153
+ end
154
+ end
155
+ rescue StandardError => e
156
+ Dependabot.logger.debug("Failed to clone and fetch hooks: #{e.message}")
157
+ nil
158
+ end
159
+
160
+ sig { params(content: String).returns(T.nilable(T::Array[T::Hash[String, T.untyped]])) }
161
+ def parse_hooks_yaml(content)
162
+ yaml = YAML.safe_load(content, aliases: true)
163
+ return nil unless yaml.is_a?(Array)
164
+
165
+ yaml.grep(Hash)
166
+ rescue Psych::SyntaxError, Psych::DisallowedClass, Psych::BadAlias => e
167
+ Dependabot.logger.debug("Failed to parse hooks YAML: #{e.message}")
168
+ nil
169
+ end
170
+
171
+ sig { returns(Dependabot::Clients::GithubWithRetries) }
172
+ def github_client
173
+ @github_client ||= T.let(
174
+ Dependabot::Clients::GithubWithRetries.for_github_dot_com(credentials: credentials),
175
+ T.nilable(Dependabot::Clients::GithubWithRetries)
176
+ )
177
+ end
178
+ end
179
+ end
180
+ end
181
+ end
@@ -23,6 +23,7 @@ module Dependabot
23
23
  extend T::Sig
24
24
 
25
25
  require "dependabot/file_parsers/base/dependency_set"
26
+ require_relative "file_parser/hook_language_fetcher"
26
27
 
27
28
  CONFIG_FILE_PATTERN = /\.pre-commit(-config)?\.ya?ml$/i
28
29
  ECOSYSTEM = "pre_commit"
@@ -134,6 +135,7 @@ module Dependabot
134
135
  def parse_additional_dependencies(repo, file)
135
136
  dependencies = []
136
137
  repo_url = repo["repo"]
138
+ revision = repo["rev"]
137
139
 
138
140
  return dependencies if repo_url.nil? || %w(local meta).include?(repo_url)
139
141
 
@@ -141,7 +143,7 @@ module Dependabot
141
143
  hooks.each do |hook|
142
144
  next unless hook.is_a?(Hash)
143
145
 
144
- hook_deps = parse_hook_additional_dependencies(hook, repo_url, file)
146
+ hook_deps = parse_hook_additional_dependencies(hook, repo_url, revision, file)
145
147
  dependencies.concat(hook_deps)
146
148
  end
147
149
 
@@ -152,18 +154,24 @@ module Dependabot
152
154
  params(
153
155
  hook: T::Hash[String, T.untyped],
154
156
  repo_url: String,
157
+ revision: T.nilable(String),
155
158
  file: Dependabot::DependencyFile
156
159
  ).returns(T::Array[Dependabot::Dependency])
157
160
  end
158
- def parse_hook_additional_dependencies(hook, repo_url, file)
161
+ def parse_hook_additional_dependencies(hook, repo_url, revision, file)
159
162
  dependencies = []
163
+ hook_id = hook["id"]
160
164
 
161
- return dependencies unless hook["id"]
165
+ return dependencies unless hook_id
162
166
 
163
167
  additional_deps = hook.fetch("additional_dependencies", [])
164
168
  return dependencies if additional_deps.empty?
165
169
 
166
- parser = LANGUAGE_PARSERS[hook["language"]]
170
+ # Get language from local config first, then try fetching from hook source repo
171
+ language = resolve_hook_language(hook, repo_url, revision, hook_id)
172
+ return dependencies unless language
173
+
174
+ parser = LANGUAGE_PARSERS[language]
167
175
  return dependencies unless parser
168
176
 
169
177
  additional_deps.each do |dep_string|
@@ -181,10 +189,10 @@ module Dependabot
181
189
  file: file.name,
182
190
  source: {
183
191
  type: "additional_dependency",
184
- language: hook["language"],
192
+ language: language,
185
193
  package_name: parsed[:normalised_name],
186
194
  original_name: parsed[:name],
187
- hook_id: hook["id"],
195
+ hook_id: hook_id,
188
196
  hook_repo: repo_url,
189
197
  extras: parsed[:extras],
190
198
  original_string: dep_string
@@ -197,6 +205,37 @@ module Dependabot
197
205
  dependencies
198
206
  end
199
207
 
208
+ sig do
209
+ params(
210
+ hook: T::Hash[String, T.untyped],
211
+ repo_url: String,
212
+ revision: T.nilable(String),
213
+ hook_id: String
214
+ ).returns(T.nilable(String))
215
+ end
216
+ def resolve_hook_language(hook, repo_url, revision, hook_id)
217
+ # Use local language if explicitly specified
218
+ local_language = hook["language"]
219
+ return local_language if local_language
220
+
221
+ # Otherwise fetch from the hook source repository
222
+ return nil unless revision
223
+
224
+ hook_language_fetcher.fetch_language(
225
+ repo_url: repo_url,
226
+ revision: revision,
227
+ hook_id: hook_id
228
+ )
229
+ end
230
+
231
+ sig { returns(HookLanguageFetcher) }
232
+ def hook_language_fetcher
233
+ @hook_language_fetcher ||= T.let(
234
+ HookLanguageFetcher.new(credentials: credentials),
235
+ T.nilable(HookLanguageFetcher)
236
+ )
237
+ end
238
+
200
239
  sig do
201
240
  params(
202
241
  file: Dependabot::DependencyFile,
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dependabot-pre_commit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.363.0
4
+ version: 0.364.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
@@ -15,84 +15,84 @@ dependencies:
15
15
  requirements:
16
16
  - - '='
17
17
  - !ruby/object:Gem::Version
18
- version: 0.363.0
18
+ version: 0.364.0
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - '='
24
24
  - !ruby/object:Gem::Version
25
- version: 0.363.0
25
+ version: 0.364.0
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: dependabot-cargo
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
30
30
  - - '='
31
31
  - !ruby/object:Gem::Version
32
- version: 0.363.0
32
+ version: 0.364.0
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - '='
38
38
  - !ruby/object:Gem::Version
39
- version: 0.363.0
39
+ version: 0.364.0
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: dependabot-common
42
42
  requirement: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - '='
45
45
  - !ruby/object:Gem::Version
46
- version: 0.363.0
46
+ version: 0.364.0
47
47
  type: :runtime
48
48
  prerelease: false
49
49
  version_requirements: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - '='
52
52
  - !ruby/object:Gem::Version
53
- version: 0.363.0
53
+ version: 0.364.0
54
54
  - !ruby/object:Gem::Dependency
55
55
  name: dependabot-go_modules
56
56
  requirement: !ruby/object:Gem::Requirement
57
57
  requirements:
58
58
  - - '='
59
59
  - !ruby/object:Gem::Version
60
- version: 0.363.0
60
+ version: 0.364.0
61
61
  type: :runtime
62
62
  prerelease: false
63
63
  version_requirements: !ruby/object:Gem::Requirement
64
64
  requirements:
65
65
  - - '='
66
66
  - !ruby/object:Gem::Version
67
- version: 0.363.0
67
+ version: 0.364.0
68
68
  - !ruby/object:Gem::Dependency
69
69
  name: dependabot-npm_and_yarn
70
70
  requirement: !ruby/object:Gem::Requirement
71
71
  requirements:
72
72
  - - '='
73
73
  - !ruby/object:Gem::Version
74
- version: 0.363.0
74
+ version: 0.364.0
75
75
  type: :runtime
76
76
  prerelease: false
77
77
  version_requirements: !ruby/object:Gem::Requirement
78
78
  requirements:
79
79
  - - '='
80
80
  - !ruby/object:Gem::Version
81
- version: 0.363.0
81
+ version: 0.364.0
82
82
  - !ruby/object:Gem::Dependency
83
83
  name: dependabot-python
84
84
  requirement: !ruby/object:Gem::Requirement
85
85
  requirements:
86
86
  - - '='
87
87
  - !ruby/object:Gem::Version
88
- version: 0.363.0
88
+ version: 0.364.0
89
89
  type: :runtime
90
90
  prerelease: false
91
91
  version_requirements: !ruby/object:Gem::Requirement
92
92
  requirements:
93
93
  - - '='
94
94
  - !ruby/object:Gem::Version
95
- version: 0.363.0
95
+ version: 0.364.0
96
96
  - !ruby/object:Gem::Dependency
97
97
  name: debug
98
98
  requirement: !ruby/object:Gem::Requirement
@@ -323,6 +323,7 @@ files:
323
323
  - lib/dependabot/pre_commit/comment_version_helper.rb
324
324
  - lib/dependabot/pre_commit/file_fetcher.rb
325
325
  - lib/dependabot/pre_commit/file_parser.rb
326
+ - lib/dependabot/pre_commit/file_parser/hook_language_fetcher.rb
326
327
  - lib/dependabot/pre_commit/file_updater.rb
327
328
  - lib/dependabot/pre_commit/helpers.rb
328
329
  - lib/dependabot/pre_commit/metadata_finder.rb
@@ -337,7 +338,7 @@ licenses:
337
338
  - MIT
338
339
  metadata:
339
340
  bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
340
- changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.363.0
341
+ changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.364.0
341
342
  rdoc_options: []
342
343
  require_paths:
343
344
  - lib