dependabot-pre_commit 0.361.2

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.
@@ -0,0 +1,188 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+
6
+ require "dependabot/errors"
7
+ require "dependabot/file_updaters"
8
+ require "dependabot/file_updaters/base"
9
+
10
+ module Dependabot
11
+ module PreCommit
12
+ class FileUpdater < Dependabot::FileUpdaters::Base
13
+ extend T::Sig
14
+
15
+ sig { override.returns(T::Array[Dependabot::DependencyFile]) }
16
+ def updated_dependency_files
17
+ updated_files = []
18
+
19
+ dependency_files.each do |file|
20
+ next unless requirement_changed?(file, dependency)
21
+
22
+ updated_files <<
23
+ updated_file(
24
+ file: file,
25
+ content: updated_config_file_content(file)
26
+ )
27
+ end
28
+
29
+ updated_files.reject! { |f| dependency_files.include?(T.cast(f, Dependabot::DependencyFile)) }
30
+ raise "No files changed!" if updated_files.none?
31
+
32
+ updated_files
33
+ end
34
+
35
+ private
36
+
37
+ sig { returns(Dependabot::Dependency) }
38
+ def dependency
39
+ T.must(dependencies.first)
40
+ end
41
+
42
+ sig { override.void }
43
+ def check_required_files
44
+ return if dependency_files.any?
45
+
46
+ raise "No pre-commit config files!"
47
+ end
48
+
49
+ sig { params(file: Dependabot::DependencyFile).returns(String) }
50
+ def updated_config_file_content(file)
51
+ updated_requirement_pairs = requirement_pairs_for_file(file)
52
+ updated_content = T.must(file.content)
53
+
54
+ updated_requirement_pairs.each do |new_req, old_req|
55
+ updated_content = apply_requirement_update(updated_content, new_req, old_req)
56
+ end
57
+
58
+ updated_content
59
+ end
60
+
61
+ sig do
62
+ params(file: Dependabot::DependencyFile)
63
+ .returns(T::Array[[T::Hash[Symbol, T.untyped], T::Hash[Symbol, T.untyped]]])
64
+ end
65
+ def requirement_pairs_for_file(file)
66
+ pairs = dependency.requirements.zip(T.must(dependency.previous_requirements))
67
+ filtered_pairs = pairs.reject do |new_req, old_req|
68
+ next true unless old_req
69
+
70
+ file_name = T.cast(new_req[:file], T.nilable(String))
71
+ next true if file_name != file.name
72
+
73
+ new_source = T.cast(new_req[:source], T.nilable(T::Hash[Symbol, T.untyped]))
74
+ old_source = T.cast(old_req[:source], T.nilable(T::Hash[Symbol, T.untyped]))
75
+ new_source == old_source
76
+ end
77
+
78
+ filtered_pairs.map { |new_req, old_req| [new_req, T.must(old_req)] }
79
+ end
80
+
81
+ sig do
82
+ params(
83
+ content: String,
84
+ new_req: T::Hash[Symbol, T.untyped],
85
+ old_req: T::Hash[Symbol, T.untyped]
86
+ ).returns(String)
87
+ end
88
+ def apply_requirement_update(content, new_req, old_req)
89
+ new_source = T.cast(new_req.fetch(:source), T::Hash[Symbol, T.untyped])
90
+ source_type = T.cast(new_source.fetch(:type), String)
91
+
92
+ case source_type
93
+ when "git"
94
+ apply_git_requirement_update(content, new_req, old_req)
95
+ when "additional_dependency"
96
+ apply_additional_dependency_update(content, new_req, old_req)
97
+ else
98
+ content
99
+ end
100
+ end
101
+
102
+ sig do
103
+ params(
104
+ content: String,
105
+ new_req: T::Hash[Symbol, T.untyped],
106
+ old_req: T::Hash[Symbol, T.untyped]
107
+ ).returns(String)
108
+ end
109
+ def apply_git_requirement_update(content, new_req, old_req)
110
+ new_source = T.cast(new_req.fetch(:source), T::Hash[Symbol, T.untyped])
111
+ old_source = T.cast(old_req.fetch(:source), T::Hash[Symbol, T.untyped])
112
+ repo_url = T.cast(old_source.fetch(:url), String)
113
+ old_ref = T.cast(old_source.fetch(:ref), String)
114
+ new_ref = T.cast(new_source.fetch(:ref), String)
115
+
116
+ replace_ref_in_content(content, repo_url, old_ref, new_ref)
117
+ end
118
+
119
+ sig do
120
+ params(
121
+ content: String,
122
+ new_req: T::Hash[Symbol, T.untyped],
123
+ old_req: T::Hash[Symbol, T.untyped]
124
+ ).returns(String)
125
+ end
126
+ def apply_additional_dependency_update(content, new_req, old_req)
127
+ old_source = T.cast(old_req.fetch(:source), T::Hash[Symbol, T.untyped])
128
+ new_source = T.cast(new_req.fetch(:source), T::Hash[Symbol, T.untyped])
129
+
130
+ old_string = T.cast(old_source.fetch(:original_string), String)
131
+ new_string = T.cast(new_source.fetch(:original_string), String)
132
+
133
+ replace_additional_dependency_in_content(content, old_string, new_string)
134
+ end
135
+
136
+ sig do
137
+ params(content: String, repo_url: String, old_ref: String, new_ref: String).returns(String)
138
+ end
139
+ def replace_ref_in_content(content, repo_url, old_ref, new_ref)
140
+ current_repo = T.let(nil, T.nilable(String))
141
+
142
+ updated_lines = content.lines.map do |line|
143
+ repo_match = line.match(/^\s*-\s*repo:\s*(\S+)/)
144
+ current_repo = repo_match[1] if repo_match
145
+
146
+ if current_repo == repo_url &&
147
+ line.match?(/^\s*rev:\s+#{Regexp.escape(old_ref)}(\s*(?:#.*)?)?$/)
148
+ line.gsub(old_ref, new_ref)
149
+ else
150
+ line
151
+ end
152
+ end
153
+
154
+ updated_lines.join
155
+ end
156
+
157
+ sig do
158
+ params(content: String, old_string: String, new_string: String).returns(String)
159
+ end
160
+ def replace_additional_dependency_in_content(content, old_string, new_string)
161
+ # Use line-by-line replacement to avoid variable-length look-behind issues
162
+ # We look for the exact dependency string in various YAML formats
163
+ escaped_old = Regexp.escape(old_string)
164
+
165
+ updated_lines = content.lines.map do |line|
166
+ # Check if this line contains the dependency in additional_dependencies context
167
+ # Matches formats like:
168
+ # - types-requests==1.0.0
169
+ # - 'types-requests==1.0.0'
170
+ # - "types-requests==1.0.0"
171
+ # [..., types-requests==1.0.0, ...]
172
+ if line.match?(/^\s*-\s*['"]?#{escaped_old}['"]?\s*$/) || # Block style
173
+ line.match?(/[\[,]\s*['"]?#{escaped_old}['"]?\s*[,\]]/) || # Flow style
174
+ line.match?(/^\s*-\s*['"]?#{escaped_old}['"]?\s*#/) # With comment
175
+ line.gsub(old_string, new_string)
176
+ else
177
+ line
178
+ end
179
+ end
180
+
181
+ updated_lines.join
182
+ end
183
+ end
184
+ end
185
+ end
186
+
187
+ Dependabot::FileUpdaters
188
+ .register("pre_commit", Dependabot::PreCommit::FileUpdater)
@@ -0,0 +1,82 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ require "dependabot/dependency"
5
+ require "dependabot/errors"
6
+ require "dependabot/pre_commit/requirement"
7
+ require "dependabot/pre_commit/version"
8
+ require "sorbet-runtime"
9
+
10
+ module Dependabot
11
+ module PreCommit
12
+ module Helpers
13
+ class Githelper
14
+ extend T::Sig
15
+
16
+ sig do
17
+ params(
18
+ dependency: Dependabot::Dependency,
19
+ credentials: T::Array[Dependabot::Credential],
20
+ ignored_versions: T::Array[String],
21
+ raise_on_ignored: T::Boolean,
22
+ consider_version_branches_pinned: T::Boolean,
23
+ dependency_source_details: T.nilable(T::Hash[Symbol, String])
24
+ )
25
+ .void
26
+ end
27
+ def initialize(
28
+ dependency:,
29
+ credentials:,
30
+ ignored_versions: [],
31
+ raise_on_ignored: false,
32
+ consider_version_branches_pinned: false,
33
+ dependency_source_details: nil
34
+ )
35
+ @dependency = dependency
36
+ @credentials = credentials
37
+ @ignored_versions = ignored_versions
38
+ @raise_on_ignored = raise_on_ignored
39
+ @consider_version_branches_pinned = consider_version_branches_pinned
40
+ @dependency_source_details = dependency_source_details
41
+ end
42
+
43
+ sig { returns(Dependabot::Dependency) }
44
+ attr_reader :dependency
45
+
46
+ sig { returns(T::Array[Dependabot::Credential]) }
47
+ attr_reader :credentials
48
+
49
+ sig { returns(T::Array[String]) }
50
+ attr_reader :ignored_versions
51
+
52
+ sig { returns(T::Boolean) }
53
+ attr_reader :raise_on_ignored
54
+
55
+ sig { returns(Dependabot::GitCommitChecker) }
56
+ def git_commit_checker
57
+ @git_commit_checker ||= T.let(
58
+ git_commit_checker_for(nil),
59
+ T.nilable(Dependabot::GitCommitChecker)
60
+ )
61
+ end
62
+
63
+ sig { params(source: T.nilable(T::Hash[Symbol, String])).returns(Dependabot::GitCommitChecker) }
64
+ def git_commit_checker_for(source)
65
+ @git_commit_checkers ||= T.let(
66
+ {},
67
+ T.nilable(T::Hash[T.nilable(T::Hash[Symbol, String]), Dependabot::GitCommitChecker])
68
+ )
69
+
70
+ @git_commit_checkers[source] ||= Dependabot::GitCommitChecker.new(
71
+ dependency: dependency,
72
+ credentials: credentials,
73
+ ignored_versions: ignored_versions,
74
+ raise_on_ignored: raise_on_ignored,
75
+ consider_version_branches_pinned: @consider_version_branches_pinned,
76
+ dependency_source_details: source || @dependency_source_details
77
+ )
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,28 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ # NOTE: This file was scaffolded automatically but is OPTIONAL.
5
+ # If you don't need custom metadata finding logic (changelogs, release notes, etc.),
6
+ # you can safely delete this file and remove the require from lib/dependabot/pre_commit.rb
7
+
8
+ require "dependabot/metadata_finders"
9
+ require "dependabot/metadata_finders/base"
10
+
11
+ module Dependabot
12
+ module PreCommit
13
+ class MetadataFinder < Dependabot::MetadataFinders::Base
14
+ extend T::Sig
15
+
16
+ private
17
+
18
+ sig { override.returns(T.nilable(Dependabot::Source)) }
19
+ def look_up_source
20
+ # TODO: Implement custom source lookup logic if needed
21
+ # Otherwise, delete this file and the require in the main registration file
22
+ nil
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ Dependabot::MetadataFinders.register("pre_commit", Dependabot::PreCommit::MetadataFinder)
@@ -0,0 +1,199 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+ require "dependabot/errors"
6
+ require "dependabot/pre_commit/helpers"
7
+ require "dependabot/pre_commit/requirement"
8
+ require "dependabot/pre_commit/update_checker"
9
+ require "dependabot/pre_commit/version"
10
+ require "dependabot/shared_helpers"
11
+
12
+ module Dependabot
13
+ module PreCommit
14
+ module Package
15
+ class PackageDetailsFetcher
16
+ extend T::Sig
17
+
18
+ sig do
19
+ params(
20
+ dependency: Dependabot::Dependency,
21
+ credentials: T::Array[Dependabot::Credential],
22
+ ignored_versions: T::Array[String],
23
+ raise_on_ignored: T::Boolean
24
+ ).void
25
+ end
26
+ def initialize(
27
+ dependency:,
28
+ credentials:,
29
+ ignored_versions: [],
30
+ raise_on_ignored: false
31
+ )
32
+ @dependency = dependency
33
+ @credentials = credentials
34
+ @raise_on_ignored = raise_on_ignored
35
+ @ignored_versions = ignored_versions
36
+
37
+ @git_helper = T.let(git_helper, Dependabot::PreCommit::Helpers::Githelper)
38
+ end
39
+
40
+ sig { returns(Dependabot::Dependency) }
41
+ attr_reader :dependency
42
+
43
+ sig { returns(T::Array[Dependabot::Credential]) }
44
+ attr_reader :credentials
45
+
46
+ sig { returns(T::Array[String]) }
47
+ attr_reader :ignored_versions
48
+
49
+ sig { returns(T::Boolean) }
50
+ attr_reader :raise_on_ignored
51
+
52
+ sig { returns(T.nilable(T.any(Dependabot::Version, String))) }
53
+ def release_list_for_git_dependency
54
+ return unless git_dependency?
55
+ return current_commit unless git_commit_checker.pinned?
56
+
57
+ version_tag_release || commit_sha_release
58
+ end
59
+
60
+ sig { returns(T.nilable(T.any(Dependabot::Version, String))) }
61
+ def version_tag_release
62
+ return unless git_commit_checker.pinned_ref_looks_like_version? && latest_version_tag
63
+
64
+ latest_version = latest_version_tag&.fetch(:version)
65
+ return current_version if shortened_semver_eq?(dependency.version, latest_version.to_s)
66
+
67
+ latest_version
68
+ end
69
+
70
+ sig { returns(T.nilable(T.any(Dependabot::Version, String))) }
71
+ def commit_sha_release
72
+ return unless git_commit_checker.pinned_ref_looks_like_commit_sha?
73
+
74
+ # Prioritize tagged releases over latest commits
75
+ # If latest_version_tag exists, use it (even if current SHA doesn't have a tag)
76
+ return latest_version_tag&.fetch(:version) if latest_version_tag
77
+
78
+ # Only fall back to latest commit if no tags exist
79
+ latest_commit_for_pinned_ref
80
+ end
81
+
82
+ sig { returns(T.nilable(T::Hash[Symbol, T.untyped])) }
83
+ def latest_version_tag
84
+ @latest_version_tag ||= T.let(
85
+ begin
86
+ return git_commit_checker.local_tag_for_latest_version if dependency.version.nil?
87
+
88
+ ref = git_commit_checker.local_ref_for_latest_version_matching_existing_precision
89
+ return ref if ref && current_version && ref.fetch(:version) > current_version
90
+
91
+ git_commit_checker.local_ref_for_latest_version_lower_precision
92
+ end,
93
+ T.nilable(T::Hash[Symbol, T.untyped])
94
+ )
95
+ end
96
+
97
+ private
98
+
99
+ sig { returns(T.nilable(String)) }
100
+ def current_commit
101
+ git_commit_checker.head_commit_for_current_branch
102
+ end
103
+
104
+ sig { params(base: T.nilable(String), other: T.nilable(String)).returns(T::Boolean) }
105
+ def shortened_semver_eq?(base, other)
106
+ return false unless base && other
107
+
108
+ base_split = base.split(".")
109
+ other_split = other.split(".")
110
+ return false unless base_split.length <= other_split.length
111
+
112
+ other_split[0..(base_split.length - 1)] == base_split
113
+ end
114
+
115
+ sig { returns(T.nilable(Dependabot::Version)) }
116
+ def current_version
117
+ @current_version ||= T.let(dependency.numeric_version, T.nilable(Dependabot::Version))
118
+ end
119
+
120
+ sig { returns(T.nilable(String)) }
121
+ def latest_commit_for_pinned_ref
122
+ @latest_commit_for_pinned_ref ||= T.let(
123
+ begin
124
+ head_commit_for_ref_sha = git_commit_checker.head_commit_for_pinned_ref
125
+ if head_commit_for_ref_sha
126
+ head_commit_for_ref_sha
127
+ else
128
+ url = git_commit_checker.dependency_source_details&.fetch(:url)
129
+ source = T.must(Source.from_url(url))
130
+
131
+ SharedHelpers.in_a_temporary_directory(File.dirname(source.repo)) do |temp_dir|
132
+ repo_contents_path = File.join(temp_dir, File.basename(source.repo))
133
+
134
+ SharedHelpers.run_shell_command("git clone --no-recurse-submodules #{url} #{repo_contents_path}")
135
+
136
+ Dir.chdir(repo_contents_path) do
137
+ ref_branch = find_container_branch(git_commit_checker.dependency_source_details&.fetch(:ref))
138
+ git_commit_checker.head_commit_for_local_branch(ref_branch) if ref_branch
139
+ end
140
+ end
141
+ end
142
+ end,
143
+ T.nilable(String)
144
+ )
145
+ end
146
+
147
+ sig { params(sha: String).returns(T.nilable(String)) }
148
+ def find_container_branch(sha)
149
+ branches_including_ref = SharedHelpers.run_shell_command(
150
+ "git branch --remotes --contains #{sha}",
151
+ fingerprint: "git branch --remotes --contains <sha>"
152
+ ).split("\n").map { |branch| branch.strip.gsub("origin/", "") }
153
+ return if branches_including_ref.empty?
154
+
155
+ current_branch = branches_including_ref.find { |branch| branch.start_with?("HEAD -> ") }
156
+
157
+ if current_branch
158
+ current_branch.delete_prefix("HEAD -> ")
159
+ elsif branches_including_ref.size > 1
160
+ raise "Multiple ambiguous branches (#{branches_including_ref.join(', ')}) include #{sha}!"
161
+ else
162
+ branches_including_ref.first
163
+ end
164
+ end
165
+
166
+ sig do
167
+ params(tags_array: T::Array[T::Hash[Symbol, T.untyped]]).returns(T::Array[T::Hash[Symbol, T.untyped]])
168
+ end
169
+ def filter_lower_tags(tags_array)
170
+ return tags_array unless current_version
171
+
172
+ tags_array.select { |tag| tag.fetch(:version) > current_version }
173
+ end
174
+
175
+ sig { returns(T::Boolean) }
176
+ def git_dependency?
177
+ git_commit_checker.git_dependency?
178
+ end
179
+
180
+ sig { returns(Dependabot::GitCommitChecker) }
181
+ def git_commit_checker
182
+ @git_commit_checker ||= T.let(git_helper.git_commit_checker, T.nilable(Dependabot::GitCommitChecker))
183
+ end
184
+
185
+ sig { returns(Dependabot::PreCommit::Helpers::Githelper) }
186
+ def git_helper
187
+ Helpers::Githelper.new(
188
+ dependency: dependency,
189
+ credentials: credentials,
190
+ ignored_versions: ignored_versions,
191
+ raise_on_ignored: raise_on_ignored,
192
+ consider_version_branches_pinned: false,
193
+ dependency_source_details: nil
194
+ )
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,29 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+ require "dependabot/pre_commit/version"
6
+ require "dependabot/ecosystem"
7
+ require "dependabot/pre_commit/requirement"
8
+
9
+ module Dependabot
10
+ module PreCommit
11
+ class PackageManager < Dependabot::Ecosystem::VersionManager
12
+ extend T::Sig
13
+
14
+ # The package manager name for Pre-commit
15
+ NAME = T.let("pre_commit", String)
16
+
17
+ # The version of the package manager
18
+ VERSION = T.let("1.0.0", String)
19
+
20
+ sig { void }
21
+ def initialize
22
+ super(
23
+ name: NAME,
24
+ version: Version.new(VERSION)
25
+ )
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,23 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+
6
+ require "dependabot/requirement"
7
+ require "dependabot/utils"
8
+
9
+ module Dependabot
10
+ module PreCommit
11
+ class Requirement < Dependabot::Requirement
12
+ extend T::Sig
13
+
14
+ sig { override.params(requirement_string: T.nilable(String)).returns(T::Array[Requirement]) }
15
+ def self.requirements_array(requirement_string)
16
+ [new(requirement_string)]
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ Dependabot::Utils
23
+ .register_requirement_class("pre_commit", Dependabot::PreCommit::Requirement)