dependabot-bun 0.296.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: aec5d70543afebc798276a80ab116a1375a90395a66815467b48ffc778f1287c
4
+ data.tar.gz: f44ddcdc48fee485db9e56d97c06040e9bcf597b42b057be8a98ca4eadbc7665
5
+ SHA512:
6
+ metadata.gz: ad382c4264db5281a25cd912a5379316e175efef2c4188e89d05699eeffc753d6114fd9046ba7fafe5bf979fd83c7ad5ec7cc2bec3237aa477d42bb19dd3b7c8
7
+ data.tar.gz: fd61ab1f24b72c1306b2b187728cfc45581740515d7000a6026bde6c5313b3e7bf3e05af3ce0e77a3d0af530e4a09a3f5b26ab518c577ef4fef5176431e44fe3
@@ -0,0 +1,97 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ module Dependabot
5
+ module Bun
6
+ class FileFetcher < Dependabot::FileFetchers::Base
7
+ include Javascript::FileFetcherHelper
8
+ extend T::Sig
9
+ extend T::Helpers
10
+
11
+ sig { override.params(filenames: T::Array[String]).returns(T::Boolean) }
12
+ def self.required_files_in?(filenames)
13
+ filenames.include?("package.json")
14
+ end
15
+
16
+ sig { override.returns(String) }
17
+ def self.required_files_message
18
+ "Repo must contain a package.json."
19
+ end
20
+
21
+ sig { override.returns(T.nilable(T::Hash[Symbol, T.untyped])) }
22
+ def ecosystem_versions
23
+ return unknown_ecosystem_versions unless ecosystem_enabled?
24
+
25
+ {
26
+ package_managers: {
27
+ "bun" => 1
28
+ }
29
+ }
30
+ end
31
+
32
+ sig { override.returns(T::Array[DependencyFile]) }
33
+ def fetch_files
34
+ fetched_files = T.let([], T::Array[DependencyFile])
35
+ fetched_files << package_json(self)
36
+ fetched_files += bun_files if ecosystem_enabled?
37
+ fetched_files += workspace_package_jsons(self)
38
+ fetched_files += path_dependencies(self, fetched_files)
39
+
40
+ fetched_files.uniq
41
+ end
42
+
43
+ sig { params(filename: String, fetch_submodules: T::Boolean).returns(DependencyFile) }
44
+ def fetch_file(filename, fetch_submodules: false)
45
+ fetch_file_from_host(filename, fetch_submodules: fetch_submodules)
46
+ end
47
+
48
+ sig do
49
+ params(
50
+ dir: T.any(Pathname, String),
51
+ ignore_base_directory: T::Boolean,
52
+ raise_errors: T::Boolean,
53
+ fetch_submodules: T::Boolean
54
+ )
55
+ .returns(T::Array[T.untyped])
56
+ end
57
+ def fetch_repo_contents(dir: ".", ignore_base_directory: false, raise_errors: true, fetch_submodules: false)
58
+ repo_contents(dir: dir, ignore_base_directory:, raise_errors:, fetch_submodules:)
59
+ end
60
+
61
+ private
62
+
63
+ sig { returns(T::Array[DependencyFile]) }
64
+ def bun_files
65
+ [bun_lock].compact
66
+ end
67
+
68
+ sig { returns(T.nilable(DependencyFile)) }
69
+ def bun_lock
70
+ return @bun_lock if defined?(@bun_lock)
71
+
72
+ @bun_lock ||= T.let(fetch_file_if_present(PackageManager::LOCKFILE_NAME), T.nilable(DependencyFile))
73
+
74
+ return @bun_lock if @bun_lock || directory == "/"
75
+
76
+ @bun_lock = fetch_file_from_parent_directories(self, PackageManager::LOCKFILE_NAME)
77
+ end
78
+
79
+ sig { returns(T::Boolean) }
80
+ def ecosystem_enabled?
81
+ allow_beta_ecosystems? && Experiments.enabled?(:enable_bun_ecosystem)
82
+ end
83
+
84
+ sig { returns(T::Hash[Symbol, String]) }
85
+ def unknown_ecosystem_versions
86
+ {
87
+ package_managers: {
88
+ "unknown" => 0
89
+ }
90
+ }
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ Dependabot::FileFetchers
97
+ .register(Dependabot::Bun::ECOSYSTEM, Dependabot::Bun::FileFetcher)
@@ -0,0 +1,148 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "yaml"
5
+ require "sorbet-runtime"
6
+
7
+ module Dependabot
8
+ module Bun
9
+ class FileParser < Dependabot::FileParsers::Base
10
+ class BunLock
11
+ extend T::Sig
12
+
13
+ sig { params(dependency_file: DependencyFile).void }
14
+ def initialize(dependency_file)
15
+ @dependency_file = dependency_file
16
+ end
17
+
18
+ sig { returns(T::Hash[String, T.untyped]) }
19
+ def parsed
20
+ @parsed ||= begin
21
+ content = begin
22
+ # Since bun.lock is a JSONC file, which is a subset of YAML, we can use YAML to parse it
23
+ YAML.load(T.must(@dependency_file.content))
24
+ rescue Psych::SyntaxError => e
25
+ raise_invalid!("malformed JSONC at line #{e.line}, column #{e.column}")
26
+ end
27
+ raise_invalid!("expected to be an object") unless content.is_a?(Hash)
28
+
29
+ version = content["lockfileVersion"]
30
+ raise_invalid!("expected 'lockfileVersion' to be an integer") unless version.is_a?(Integer)
31
+ raise_invalid!("expected 'lockfileVersion' to be >= 0") unless version >= 0
32
+
33
+ T.let(content, T.untyped)
34
+ end
35
+ end
36
+
37
+ sig { returns(Dependabot::FileParsers::Base::DependencySet) }
38
+ def dependencies
39
+ dependency_set = Dependabot::FileParsers::Base::DependencySet.new
40
+
41
+ # bun.lock v0 format:
42
+ # https://github.com/oven-sh/bun/blob/c130df6c589fdf28f9f3c7f23ed9901140bc9349/src/install/bun.lock.zig#L595-L605
43
+
44
+ packages = parsed["packages"]
45
+ raise_invalid!("expected 'packages' to be an object") unless packages.is_a?(Hash)
46
+
47
+ packages.each do |key, details|
48
+ raise_invalid!("expected 'packages.#{key}' to be an array") unless details.is_a?(Array)
49
+
50
+ resolution = details.first
51
+ raise_invalid!("expected 'packages.#{key}[0]' to be a string") unless resolution.is_a?(String)
52
+
53
+ name, version = resolution.split(/(?<=\w)\@/)
54
+ next if name.empty?
55
+
56
+ semver = Version.semver_for(version)
57
+ next unless semver
58
+
59
+ dependency_set << Dependency.new(
60
+ name: name,
61
+ version: semver.to_s,
62
+ package_manager: "npm_and_yarn",
63
+ requirements: []
64
+ )
65
+ end
66
+
67
+ dependency_set
68
+ end
69
+
70
+ sig do
71
+ params(dependency_name: String, requirement: T.untyped, _manifest_name: String)
72
+ .returns(T.nilable(T::Hash[String, T.untyped]))
73
+ end
74
+ def details(dependency_name, requirement, _manifest_name)
75
+ packages = parsed["packages"]
76
+ return unless packages.is_a?(Hash)
77
+
78
+ candidates =
79
+ packages
80
+ .select { |name, _| name == dependency_name }
81
+ .values
82
+
83
+ # If there's only one entry for this dependency, use it, even if
84
+ # the requirement in the lockfile doesn't match
85
+ if candidates.one?
86
+ parse_details(candidates.first)
87
+ else
88
+ candidate = candidates.find do |label, _|
89
+ label.scan(/(?<=\w)\@(?:npm:)?([^\s,]+)/).flatten.include?(requirement)
90
+ end&.last
91
+ parse_details(candidate)
92
+ end
93
+ end
94
+
95
+ private
96
+
97
+ sig { params(message: String).void }
98
+ def raise_invalid!(message)
99
+ raise Dependabot::DependencyFileNotParseable.new(@dependency_file.path, "Invalid bun.lock file: #{message}")
100
+ end
101
+
102
+ sig do
103
+ params(entry: T.nilable(T::Array[T.untyped])).returns(T.nilable(T::Hash[String, T.untyped]))
104
+ end
105
+ def parse_details(entry)
106
+ return unless entry.is_a?(Array)
107
+
108
+ # Either:
109
+ # - "{name}@{version}", registry, details, integrity
110
+ # - "{name}@{resolution}", details
111
+ resolution = entry.first
112
+ return unless resolution.is_a?(String)
113
+
114
+ name, version = resolution.split(/(?<=\w)\@/)
115
+ semver = Version.semver_for(version)
116
+
117
+ if semver
118
+ registry, details, integrity = entry[1..3]
119
+ {
120
+ "name" => name,
121
+ "version" => semver.to_s,
122
+ "registry" => registry,
123
+ "details" => details,
124
+ "integrity" => integrity
125
+ }
126
+ else
127
+ details = entry[1]
128
+ {
129
+ "name" => name,
130
+ "resolution" => version,
131
+ "details" => details
132
+ }
133
+ end
134
+ end
135
+ end
136
+
137
+ sig { override.returns(T::Array[Dependabot::Dependency]) }
138
+ def parse
139
+ []
140
+ end
141
+
142
+ private
143
+
144
+ sig { override.void }
145
+ def check_required_files; end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,79 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ module Dependabot
5
+ module Bun
6
+ module Helpers
7
+ extend T::Sig
8
+
9
+ # BUN Version Constants
10
+ BUN_V1 = 1
11
+ BUN_DEFAULT_VERSION = BUN_V1
12
+
13
+ sig { params(_bun_lock: T.nilable(DependencyFile)).returns(Integer) }
14
+ def self.bun_version_numeric(_bun_lock)
15
+ BUN_DEFAULT_VERSION
16
+ end
17
+
18
+ sig { returns(T.nilable(String)) }
19
+ def self.bun_version
20
+ run_bun_command("--version", fingerprint: "--version").strip
21
+ rescue StandardError => e
22
+ Dependabot.logger.error("Error retrieving Bun version: #{e.message}")
23
+ nil
24
+ end
25
+
26
+ sig { params(command: String, fingerprint: T.nilable(String)).returns(String) }
27
+ def self.run_bun_command(command, fingerprint: nil)
28
+ full_command = "bun #{command}"
29
+
30
+ Dependabot.logger.info("Running bun command: #{full_command}")
31
+
32
+ result = Dependabot::SharedHelpers.run_shell_command(
33
+ full_command,
34
+ fingerprint: "bun #{fingerprint || command}"
35
+ )
36
+
37
+ Dependabot.logger.info("Command executed successfully: #{full_command}")
38
+ result
39
+ rescue StandardError => e
40
+ Dependabot.logger.error("Error running bun command: #{full_command}, Error: #{e.message}")
41
+ raise
42
+ end
43
+
44
+ # Fetch the currently installed version of the package manager directly
45
+ # from the system
46
+ sig { params(name: String).returns(String) }
47
+ def self.local_package_manager_version(name)
48
+ Dependabot::SharedHelpers.run_shell_command(
49
+ "#{name} -v",
50
+ fingerprint: "#{name} -v"
51
+ ).strip
52
+ end
53
+
54
+ # Run single command on package manager returning stdout/stderr
55
+ sig do
56
+ params(
57
+ name: String,
58
+ command: String,
59
+ fingerprint: T.nilable(String)
60
+ ).returns(String)
61
+ end
62
+ def self.package_manager_run_command(name, command, fingerprint: nil)
63
+ return run_bun_command(command, fingerprint: fingerprint) if name == PackageManager::NAME
64
+
65
+ # TODO: remove this method and just use the one in the PackageManager class
66
+ "noop"
67
+ end
68
+
69
+ sig { params(dependency_set: Dependabot::FileParsers::Base::DependencySet).returns(T::Array[Dependency]) }
70
+ def self.dependencies_with_all_versions_metadata(dependency_set)
71
+ # TODO: Check if we still need this method
72
+ dependency_set.dependencies.map do |dependency|
73
+ dependency.metadata[:all_versions] = dependency_set.all_versions_for_name(dependency.name)
74
+ dependency
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,45 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ require "dependabot/npm_and_yarn/package_manager"
5
+
6
+ module Dependabot
7
+ module Bun
8
+ class Language < Ecosystem::VersionManager
9
+ extend T::Sig
10
+ NAME = "node"
11
+
12
+ SUPPORTED_VERSIONS = T.let([].freeze, T::Array[Dependabot::Version])
13
+
14
+ DEPRECATED_VERSIONS = T.let([].freeze, T::Array[Dependabot::Version])
15
+
16
+ sig do
17
+ params(
18
+ detected_version: T.nilable(String),
19
+ raw_version: T.nilable(String),
20
+ requirement: T.nilable(Dependabot::NpmAndYarn::Requirement)
21
+ ).void
22
+ end
23
+ def initialize(detected_version: nil, raw_version: nil, requirement: nil)
24
+ super(
25
+ name: NAME,
26
+ detected_version: detected_version ? Version.new(detected_version) : nil,
27
+ version: raw_version ? Version.new(raw_version) : nil,
28
+ deprecated_versions: DEPRECATED_VERSIONS,
29
+ supported_versions: SUPPORTED_VERSIONS,
30
+ requirement: requirement
31
+ )
32
+ end
33
+
34
+ sig { override.returns(T::Boolean) }
35
+ def deprecated?
36
+ false
37
+ end
38
+
39
+ sig { override.returns(T::Boolean) }
40
+ def unsupported?
41
+ false
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,46 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ module Dependabot
5
+ module Bun
6
+ class PackageManager < Ecosystem::VersionManager
7
+ extend T::Sig
8
+ NAME = "bun"
9
+ LOCKFILE_NAME = "bun.lock"
10
+
11
+ # In Bun 1.1.39, the lockfile format was changed from a binary bun.lockb to a text-based bun.lock.
12
+ # https://bun.sh/blog/bun-lock-text-lockfile
13
+ MIN_SUPPORTED_VERSION = T.let(Version.new("1.1.39"), Javascript::Version)
14
+ SUPPORTED_VERSIONS = T.let([MIN_SUPPORTED_VERSION].freeze, T::Array[Javascript::Version])
15
+ DEPRECATED_VERSIONS = T.let([].freeze, T::Array[Version])
16
+
17
+ sig do
18
+ params(
19
+ detected_version: T.nilable(String),
20
+ raw_version: T.nilable(String),
21
+ requirement: T.nilable(Requirement)
22
+ ).void
23
+ end
24
+ def initialize(detected_version: nil, raw_version: nil, requirement: nil)
25
+ super(
26
+ name: NAME,
27
+ detected_version: detected_version ? Version.new(detected_version) : nil,
28
+ version: raw_version ? Version.new(raw_version) : nil,
29
+ deprecated_versions: DEPRECATED_VERSIONS,
30
+ supported_versions: SUPPORTED_VERSIONS,
31
+ requirement: requirement
32
+ )
33
+ end
34
+
35
+ sig { override.returns(T::Boolean) }
36
+ def deprecated?
37
+ false
38
+ end
39
+
40
+ sig { override.returns(T::Boolean) }
41
+ def unsupported?
42
+ false
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,14 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ module Dependabot
5
+ module Bun
6
+ class Requirement < Dependabot::Javascript::Requirement
7
+ end
8
+ end
9
+ end
10
+
11
+ Dependabot::Utils.register_requirement_class(
12
+ "bun",
13
+ Dependabot::Bun::Requirement
14
+ )
@@ -0,0 +1,12 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ module Dependabot
5
+ module Bun
6
+ class Version < Dependabot::Javascript::Version
7
+ end
8
+ end
9
+ end
10
+
11
+ Dependabot::Utils
12
+ .register_version_class("bun", Dependabot::Bun::Version)
@@ -0,0 +1,39 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ require "zeitwerk"
5
+
6
+ loader = Zeitwerk::Loader.new
7
+
8
+ # Set autoload paths for common/lib, excluding files whose content does not match the filename
9
+ loader.push_dir(File.join(__dir__, "../../../common/lib"))
10
+ loader.ignore(File.join(__dir__, "../../../common/lib/dependabot/errors.rb"))
11
+ loader.ignore(File.join(__dir__, "../../../common/lib/dependabot/logger.rb"))
12
+ loader.ignore(File.join(__dir__, "../../../common/lib/dependabot/notices.rb"))
13
+ loader.ignore(File.join(__dir__, "../../../common/lib/dependabot/clients/codecommit.rb"))
14
+
15
+ loader.push_dir(File.join(__dir__, ".."))
16
+ loader.ignore("#{__dir__}/../script", "#{__dir__}/../spec", "#{__dir__}/../dependabot-bun.gemspec")
17
+
18
+ loader.on_load do |_file|
19
+ require "json"
20
+ require "sorbet-runtime"
21
+ require "dependabot/errors"
22
+ require "dependabot/logger"
23
+ require "dependabot/notices"
24
+ require "dependabot/clients/codecommit"
25
+ end
26
+
27
+ loader.log! if ENV["DEBUG"]
28
+ loader.setup
29
+
30
+ Dependabot::PullRequestCreator::Labeler
31
+ .register_label_details("bun", name: "javascript", colour: "168700")
32
+
33
+ Dependabot::Dependency.register_production_check("bun", ->(_) { true })
34
+
35
+ module Dependabot
36
+ module Bun
37
+ ECOSYSTEM = "bun"
38
+ end
39
+ end
@@ -0,0 +1,245 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Dependabot
5
+ module Javascript
6
+ module FileFetcherHelper
7
+ include Kernel
8
+ extend T::Sig
9
+
10
+ PATH_DEPENDENCY_STARTS = T.let(%w(file: / ./ ../ ~/).freeze, [String, String, String, String, String])
11
+ PATH_DEPENDENCY_CLEAN_REGEX = /^file:|^link:/
12
+ DEPENDENCY_TYPES = T.let(%w(dependencies devDependencies optionalDependencies).freeze, T::Array[String])
13
+
14
+ sig { params(instance: Dependabot::Bun::FileFetcher).returns(T::Array[DependencyFile]) }
15
+ def workspace_package_jsons(instance)
16
+ @workspace_package_jsons ||= T.let(fetch_workspace_package_jsons(instance), T.nilable(T::Array[DependencyFile]))
17
+ end
18
+
19
+ sig do
20
+ params(instance: Dependabot::Bun::FileFetcher, fetched_files: T::Array[DependencyFile])
21
+ .returns(T::Array[DependencyFile])
22
+ end
23
+ def path_dependencies(instance, fetched_files)
24
+ package_json_files = T.let([], T::Array[DependencyFile])
25
+
26
+ path_dependency_details(instance, fetched_files).each do |name, path|
27
+ # This happens with relative paths in the package-lock. Skipping it since it results
28
+ # in /package.json which is outside of the project directory.
29
+ next if path == "file:"
30
+
31
+ path = path.gsub(PATH_DEPENDENCY_CLEAN_REGEX, "")
32
+ raise PathDependenciesNotReachable, "#{name} at #{path}" if path.start_with?("/")
33
+
34
+ filename = path
35
+ filename = File.join(filename, MANIFEST_FILENAME)
36
+ cleaned_name = Pathname.new(filename).cleanpath.to_path
37
+ next if fetched_files.map(&:name).include?(cleaned_name)
38
+
39
+ file = instance.fetch_file(filename, fetch_submodules: true)
40
+ package_json_files << file
41
+ end
42
+
43
+ if package_json_files.any?
44
+ package_json_files +=
45
+ path_dependencies(instance, fetched_files + package_json_files)
46
+ end
47
+
48
+ package_json_files.tap { |fs| fs.each { |f| f.support_file = true } }
49
+ end
50
+
51
+ sig do
52
+ params(instance: Dependabot::Bun::FileFetcher, fetched_files: T::Array[DependencyFile])
53
+ .returns(T::Array[[String, String]])
54
+ end
55
+ def path_dependency_details(instance, fetched_files)
56
+ package_json_path_deps = T.let([], T::Array[[String, String]])
57
+
58
+ fetched_files.each do |file|
59
+ package_json_path_deps +=
60
+ path_dependency_details_from_manifest(instance, file)
61
+ end
62
+
63
+ [
64
+ *package_json_path_deps
65
+ ].uniq
66
+ end
67
+
68
+ # rubocop:disable Metrics/PerceivedComplexity
69
+ # rubocop:disable Metrics/AbcSize
70
+ sig { params(instance: Dependabot::Bun::FileFetcher, file: DependencyFile).returns(T::Array[[String, String]]) }
71
+ def path_dependency_details_from_manifest(instance, file)
72
+ return [] unless file.name.end_with?(MANIFEST_FILENAME)
73
+
74
+ current_dir = file.name.rpartition("/").first
75
+ current_dir = nil if current_dir == ""
76
+
77
+ current_depth = File.join(instance.directory, file.name).split("/").count { |path| !path.empty? }
78
+ path_to_directory = "../" * current_depth
79
+
80
+ dep_types = DEPENDENCY_TYPES # TODO: Is this needed?
81
+ parsed_manifest = JSON.parse(T.must(file.content))
82
+ dependency_objects = parsed_manifest.values_at(*dep_types).compact
83
+ # Fetch yarn "file:" path "resolutions" so the lockfile can be resolved
84
+ resolution_objects = parsed_manifest.values_at("resolutions").compact
85
+ manifest_objects = dependency_objects + resolution_objects
86
+
87
+ raise Dependabot::DependencyFileNotParseable, file.path unless manifest_objects.all?(Hash)
88
+
89
+ resolution_deps = resolution_objects.flat_map(&:to_a)
90
+ .map do |path, value|
91
+ # skip dependencies that contain invalid values such as inline comments, null, etc.
92
+
93
+ unless value.is_a?(String)
94
+ Dependabot.logger.warn("File fetcher: Skipping dependency \"#{path}\" " \
95
+ "with value: \"#{value}\"")
96
+
97
+ next
98
+ end
99
+
100
+ convert_dependency_path_to_name(path, value)
101
+ end
102
+
103
+ path_starts = PATH_DEPENDENCY_STARTS
104
+ (dependency_objects.flat_map(&:to_a) + resolution_deps)
105
+ .select { |_, v| v.is_a?(String) && v.start_with?(*path_starts) }
106
+ .map do |name, path|
107
+ path = path.gsub(PATH_DEPENDENCY_CLEAN_REGEX, "")
108
+ raise PathDependenciesNotReachable, "#{name} at #{path}" if path.start_with?("/", "#{path_to_directory}..")
109
+
110
+ path = File.join(current_dir, path) unless current_dir.nil?
111
+ [name, Pathname.new(path).cleanpath.to_path]
112
+ end
113
+ rescue JSON::ParserError
114
+ raise Dependabot::DependencyFileNotParseable, file.path
115
+ end
116
+ # rubocop:enable Metrics/AbcSize
117
+ # rubocop:enable Metrics/PerceivedComplexity
118
+
119
+ # Re-write the glob name to the targeted dependency name (which is used
120
+ # in the lockfile), for example "parent-package/**/sub-dep/target-dep" >
121
+ # "target-dep"
122
+ sig { params(path: String, value: String).returns([String, String]) }
123
+ def convert_dependency_path_to_name(path, value)
124
+ # Picking the last two parts that might include a scope
125
+ parts = path.split("/").last(2)
126
+ parts.shift if parts.count == 2 && !T.must(parts.first).start_with?("@")
127
+ [parts.join("/"), value]
128
+ end
129
+
130
+ sig { params(instance: Dependabot::Bun::FileFetcher).returns(T::Array[DependencyFile]) }
131
+ def fetch_workspace_package_jsons(instance)
132
+ parsed_manifest = parsed_package_json(instance)
133
+ return [] unless parsed_manifest["workspaces"]
134
+
135
+ workspace_paths(instance, parsed_manifest["workspaces"]).filter_map do |workspace|
136
+ fetch_package_json_if_present(instance, workspace)
137
+ end
138
+ end
139
+
140
+ sig { params(instance: Dependabot::Bun::FileFetcher, workspace_object: T.untyped).returns(T::Array[String]) }
141
+ def workspace_paths(instance, workspace_object)
142
+ paths_array =
143
+ if workspace_object.is_a?(Hash)
144
+ workspace_object.values_at("packages", "nohoist").flatten.compact
145
+ elsif workspace_object.is_a?(Array) then workspace_object
146
+ else
147
+ [] # Invalid lerna.json, which must not be in use
148
+ end
149
+
150
+ paths_array.flat_map { |path| recursive_find_directories(instance, path) }
151
+ end
152
+
153
+ sig { params(instance: Dependabot::Bun::FileFetcher, glob: String).returns(T::Array[String]) }
154
+ def find_directories(instance, glob)
155
+ return [glob] unless glob.include?("*")
156
+
157
+ unglobbed_path =
158
+ glob.gsub(%r{^\./}, "").gsub(/!\(.*?\)/, "*")
159
+ .split("*")
160
+ .first&.gsub(%r{(?<=/)[^/]*$}, "") || "."
161
+
162
+ dir = instance.directory.gsub(%r{(^/|/$)}, "")
163
+
164
+ paths =
165
+ instance.fetch_repo_contents(dir: unglobbed_path, raise_errors: false)
166
+ .select { |file| file.type == "dir" }
167
+ .map { |f| f.path.gsub(%r{^/?#{Regexp.escape(dir)}/?}, "") }
168
+
169
+ matching_paths(glob, paths)
170
+ end
171
+
172
+ sig { params(glob: String, paths: T::Array[String]).returns(T::Array[String]) }
173
+ def matching_paths(glob, paths)
174
+ glob = glob.gsub(%r{^\./}, "").gsub(/!\(.*?\)/, "*")
175
+ glob = "#{glob}/*" if glob.end_with?("**")
176
+
177
+ paths.select { |filename| File.fnmatch?(glob, filename, File::FNM_PATHNAME) }
178
+ end
179
+
180
+ sig { params(instance: Dependabot::Bun::FileFetcher, glob: String, prefix: String).returns(T::Array[String]) }
181
+ def recursive_find_directories(instance, glob, prefix = "")
182
+ return [prefix + glob] unless glob.include?("*")
183
+
184
+ glob = glob.gsub(%r{^\./}, "")
185
+ glob_parts = glob.split("/")
186
+
187
+ current_glob = glob_parts.first
188
+ paths = find_directories(instance, prefix + T.must(current_glob))
189
+ next_parts = current_glob == "**" ? glob_parts : glob_parts.drop(1)
190
+ return paths if next_parts.empty?
191
+
192
+ paths += paths.flat_map do |expanded_path|
193
+ recursive_find_directories(instance, next_parts.join("/"), "#{expanded_path}/")
194
+ end
195
+
196
+ matching_paths(prefix + glob, paths)
197
+ end
198
+
199
+ sig { params(instance: Dependabot::Bun::FileFetcher, workspace: String).returns(T.nilable(DependencyFile)) }
200
+ def fetch_package_json_if_present(instance, workspace)
201
+ file = File.join(workspace, MANIFEST_FILENAME)
202
+
203
+ begin
204
+ instance.fetch_file(file)
205
+ rescue Dependabot::DependencyFileNotFound
206
+ # Not all paths matched by a workspace glob may contain a package.json
207
+ # file. Ignore if that's the case
208
+ nil
209
+ end
210
+ end
211
+
212
+ sig { params(instance: Dependabot::Bun::FileFetcher).returns(DependencyFile) }
213
+ def package_json(instance)
214
+ @package_json ||= T.let(instance.fetch_file(Javascript::MANIFEST_FILENAME), T.nilable(DependencyFile))
215
+ end
216
+
217
+ sig { params(instance: Dependabot::Bun::FileFetcher).returns(T.untyped) }
218
+ def parsed_package_json(instance)
219
+ manifest_file = package_json(instance)
220
+ parsed = JSON.parse(T.must(manifest_file.content))
221
+ raise Dependabot::DependencyFileNotParseable, manifest_file.path unless parsed.is_a?(Hash)
222
+
223
+ parsed
224
+ rescue JSON::ParserError
225
+ raise Dependabot::DependencyFileNotParseable, T.must(manifest_file).path
226
+ end
227
+
228
+ sig { params(instance: Dependabot::Bun::FileFetcher, filename: String).returns(T.nilable(DependencyFile)) }
229
+ def fetch_file_with_support(instance, filename)
230
+ instance.fetch_file(filename).tap { |f| f.support_file = true }
231
+ rescue Dependabot::DependencyFileNotFound
232
+ nil
233
+ end
234
+
235
+ sig { params(instance: Dependabot::Bun::FileFetcher, filename: String).returns(T.nilable(DependencyFile)) }
236
+ def fetch_file_from_parent_directories(instance, filename)
237
+ (1..instance.directory.split("/").count).each do |i|
238
+ file = fetch_file_with_support(instance, ("../" * i) + filename)
239
+ return file if file
240
+ end
241
+ nil
242
+ end
243
+ end
244
+ end
245
+ end
@@ -0,0 +1,141 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+
6
+ require "dependabot/requirement"
7
+ require "dependabot/utils"
8
+ require "dependabot/npm_and_yarn/version"
9
+
10
+ module Dependabot
11
+ module Javascript
12
+ class Requirement < Dependabot::Requirement
13
+ extend T::Sig
14
+
15
+ AND_SEPARATOR = /(?<=[a-zA-Z0-9*])\s+(?:&+\s+)?(?!\s*[|-])/
16
+ OR_SEPARATOR = /(?<=[a-zA-Z0-9*])\s*\|+/
17
+
18
+ # Override the version pattern to allow a 'v' prefix
19
+ quoted = OPS.keys.map { |k| Regexp.quote(k) }.join("|")
20
+ version_pattern = "v?#{Javascript::Version::VERSION_PATTERN}"
21
+
22
+ PATTERN_RAW = "\\s*(#{quoted})?\\s*(#{version_pattern})\\s*".freeze
23
+ PATTERN = /\A#{PATTERN_RAW}\z/
24
+
25
+ def self.parse(obj)
26
+ return ["=", nil] if obj.is_a?(String) && Version::VERSION_TAGS.include?(obj.strip)
27
+ return ["=", Javascript::Version.new(obj.to_s)] if obj.is_a?(Gem::Version)
28
+
29
+ unless (matches = PATTERN.match(obj.to_s))
30
+ msg = "Illformed requirement [#{obj.inspect}]"
31
+ raise BadRequirementError, msg
32
+ end
33
+
34
+ return DefaultRequirement if matches[1] == ">=" && matches[2] == "0"
35
+
36
+ [matches[1] || "=", Javascript::Version.new(T.must(matches[2]))]
37
+ end
38
+
39
+ # Returns an array of requirements. At least one requirement from the
40
+ # returned array must be satisfied for a version to be valid.
41
+ sig { override.params(requirement_string: T.nilable(String)).returns(T::Array[Requirement]) }
42
+ def self.requirements_array(requirement_string)
43
+ return [new(nil)] if requirement_string.nil?
44
+
45
+ # Removing parentheses is technically wrong but they are extremely
46
+ # rarely used.
47
+ # TODO: Handle complicated parenthesised requirements
48
+ requirement_string = requirement_string.gsub(/[()]/, "")
49
+ requirement_string.strip.split(OR_SEPARATOR).map do |req_string|
50
+ requirements = req_string.strip.split(AND_SEPARATOR)
51
+ new(requirements)
52
+ end
53
+ end
54
+
55
+ def initialize(*requirements)
56
+ requirements = requirements.flatten
57
+ .flat_map { |req_string| req_string.split(",").map(&:strip) }
58
+ .flat_map { |req_string| convert_js_constraint_to_ruby_constraint(req_string) }
59
+
60
+ super(requirements)
61
+ end
62
+
63
+ private
64
+
65
+ def convert_js_constraint_to_ruby_constraint(req_string)
66
+ return req_string if req_string.match?(/^([A-Za-uw-z]|v[^\d])/)
67
+
68
+ req_string = req_string.gsub(/(?:\.|^)[xX*]/, "")
69
+
70
+ if req_string.empty? then ">= 0"
71
+ elsif req_string.start_with?("~>") then req_string
72
+ elsif req_string.start_with?("=") then req_string.gsub(/^=*/, "")
73
+ elsif req_string.start_with?("~") then convert_tilde_req(req_string)
74
+ elsif req_string.start_with?("^") then convert_caret_req(req_string)
75
+ elsif req_string.include?(" - ") then convert_hyphen_req(req_string)
76
+ elsif req_string.match?(/[<>]/) then req_string
77
+ else
78
+ ruby_range(req_string)
79
+ end
80
+ end
81
+
82
+ def convert_tilde_req(req_string)
83
+ version = req_string.gsub(/^~\>?[\s=]*/, "")
84
+ parts = version.split(".")
85
+ parts << "0" if parts.count < 3
86
+ "~> #{parts.join('.')}"
87
+ end
88
+
89
+ def convert_hyphen_req(req_string)
90
+ lower_bound, upper_bound = req_string.split(/\s+-\s+/)
91
+ lower_bound_parts = lower_bound.split(".")
92
+ lower_bound_parts.fill("0", lower_bound_parts.length...3)
93
+
94
+ upper_bound_parts = upper_bound.split(".")
95
+ upper_bound_range =
96
+ if upper_bound_parts.length < 3
97
+ # When upper bound is a partial version treat these as an X-range
98
+ upper_bound_parts[-1] = upper_bound_parts[-1].to_i + 1 if upper_bound_parts[-1].to_i.positive?
99
+ upper_bound_parts.fill("0", upper_bound_parts.length...3)
100
+ "< #{upper_bound_parts.join('.')}.a"
101
+ else
102
+ "<= #{upper_bound_parts.join('.')}"
103
+ end
104
+
105
+ [">= #{lower_bound_parts.join('.')}", upper_bound_range]
106
+ end
107
+
108
+ def ruby_range(req_string)
109
+ parts = req_string.split(".")
110
+ # If we have three or more parts then this is an exact match
111
+ return req_string if parts.count >= 3
112
+
113
+ # If we have fewer than three parts we do a partial match
114
+ parts << "0"
115
+ "~> #{parts.join('.')}"
116
+ end
117
+
118
+ def convert_caret_req(req_string)
119
+ version = req_string.gsub(/^\^[\s=]*/, "")
120
+ parts = version.split(".")
121
+ parts.fill("x", parts.length...3)
122
+ first_non_zero = parts.find { |d| d != "0" }
123
+ first_non_zero_index =
124
+ first_non_zero ? parts.index(first_non_zero) : parts.count - 1
125
+ # If the requirement has a blank minor or patch version increment the
126
+ # previous index value with 1
127
+ first_non_zero_index -= 1 if first_non_zero == "x"
128
+ upper_bound = parts.map.with_index do |part, i|
129
+ if i < first_non_zero_index then part
130
+ elsif i == first_non_zero_index then (part.to_i + 1).to_s
131
+ elsif i > first_non_zero_index && i == 2 then "0.a"
132
+ else
133
+ 0
134
+ end
135
+ end.join(".")
136
+
137
+ [">= #{version}", "< #{upper_bound}"]
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,135 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "dependabot/version"
5
+ require "dependabot/utils"
6
+ require "sorbet-runtime"
7
+
8
+ # JavaScript pre-release versions use 1.0.1-rc1 syntax, which Gem::Version
9
+ # converts into 1.0.1.pre.rc1. We override the `to_s` method to stop that
10
+ # alteration.
11
+ #
12
+ # See https://semver.org/ for details of node's version syntax.
13
+
14
+ module Dependabot
15
+ module Javascript
16
+ class Version < Dependabot::Version
17
+ extend T::Sig
18
+
19
+ sig { returns(T.nilable(String)) }
20
+ attr_reader :build_info
21
+
22
+ # These are possible npm versioning tags that can be used in place of a version.
23
+ # See https://docs.npmjs.com/cli/v10/commands/npm-dist-tag#purpose for more details.
24
+ VERSION_TAGS = T.let([
25
+ "alpha", # Alpha version, early testing phase
26
+ "beta", # Beta version, more stable than alpha
27
+ "canary", # Canary version, often used for cutting-edge builds
28
+ "dev", # Development version, ongoing development
29
+ "experimental", # Experimental version, unstable and new features
30
+ "latest", # Latest stable version, used by npm to identify the current version of a package
31
+ "legacy", # Legacy version, older version maintained for compatibility
32
+ "next", # Next version, used by some projects to identify the upcoming version
33
+ "nightly", # Nightly build, daily builds often including latest changes
34
+ "rc", # Release candidate, potential final version
35
+ "release", # General release version
36
+ "stable" # Stable version, thoroughly tested and stable
37
+ ].freeze.map(&:freeze), T::Array[String])
38
+
39
+ VERSION_PATTERN = T.let(Gem::Version::VERSION_PATTERN + '(\+[0-9a-zA-Z\-.]+)?', String)
40
+ ANCHORED_VERSION_PATTERN = /\A\s*(#{VERSION_PATTERN})?\s*\z/
41
+
42
+ sig { override.params(version: VersionParameter).returns(T::Boolean) }
43
+ def self.correct?(version)
44
+ version = version.gsub(/^v/, "") if version.is_a?(String)
45
+
46
+ return false if version.nil?
47
+
48
+ version.to_s.match?(ANCHORED_VERSION_PATTERN)
49
+ end
50
+
51
+ sig { params(version: VersionParameter).returns(VersionParameter) }
52
+ def self.semver_for(version)
53
+ # The next two lines are to guard against improperly formatted
54
+ # versions in a lockfile, such as an empty string or additional
55
+ # characters. NPM/yarn fixes these when running an update, so we can
56
+ # safely ignore these versions.
57
+ return if version == ""
58
+ return unless correct?(version)
59
+
60
+ version
61
+ end
62
+
63
+ sig { override.params(version: VersionParameter).void }
64
+ def initialize(version)
65
+ version = clean_version(version)
66
+
67
+ @version_string = T.let(version.to_s, String)
68
+
69
+ @build_info = T.let(nil, T.nilable(String))
70
+
71
+ version, @build_info = version.to_s.split("+") if version.to_s.include?("+")
72
+
73
+ super(T.must(version))
74
+ end
75
+
76
+ sig { params(version: VersionParameter).returns(VersionParameter) }
77
+ def clean_version(version)
78
+ # Check if version is a string before attempting to match
79
+ if version.is_a?(String)
80
+ # Matches @ followed by x.y.z (digits separated by dots)
81
+ if (match = version.match(/@(\d+\.\d+\.\d+)/))
82
+ version = match[1] # Just "4.5.3"
83
+
84
+ # Extract version in case the output contains Corepack verbose data
85
+ elsif version.include?("Corepack")
86
+ version = T.must(T.must(version.tr("\n", " ").match(/(\d+\.\d+\.\d+)/))[-1])
87
+ end
88
+ version = version&.gsub(/^v/, "")
89
+ end
90
+
91
+ version
92
+ end
93
+
94
+ sig { override.params(version: VersionParameter).returns(Dependabot::Javascript::Version) }
95
+ def self.new(version)
96
+ T.cast(super, Dependabot::Javascript::Version)
97
+ end
98
+
99
+ sig { returns(Integer) }
100
+ def major
101
+ @major ||= T.let(segments[0].to_i, T.nilable(Integer))
102
+ end
103
+
104
+ sig { returns(Integer) }
105
+ def minor
106
+ @minor ||= T.let(segments[1].to_i, T.nilable(Integer))
107
+ end
108
+
109
+ sig { returns(Integer) }
110
+ def patch
111
+ @patch ||= T.let(segments[2].to_i, T.nilable(Integer))
112
+ end
113
+
114
+ sig { params(other: Dependabot::Javascript::Version).returns(T::Boolean) }
115
+ def backwards_compatible_with?(other)
116
+ case major
117
+ when 0
118
+ self == other
119
+ else
120
+ major == other.major && minor >= other.minor
121
+ end
122
+ end
123
+
124
+ sig { override.returns(String) }
125
+ def to_s
126
+ @version_string
127
+ end
128
+
129
+ sig { override.returns(String) }
130
+ def inspect
131
+ "#<#{self.class} #{@version_string}>"
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,8 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ module Dependabot
5
+ module Javascript
6
+ MANIFEST_FILENAME = "package.json"
7
+ end
8
+ end
metadata ADDED
@@ -0,0 +1,296 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dependabot-bun
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.296.0
5
+ platform: ruby
6
+ authors:
7
+ - Dependabot
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2025-02-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: dependabot-common
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 0.296.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 0.296.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: zeitwerk
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.7'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.7'
41
+ - !ruby/object:Gem::Dependency
42
+ name: debug
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 1.9.2
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 1.9.2
55
+ - !ruby/object:Gem::Dependency
56
+ name: gpgme
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '13'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '13'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.12'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.12'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec-its
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.3'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.3'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rspec-sorbet
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 1.9.2
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 1.9.2
125
+ - !ruby/object:Gem::Dependency
126
+ name: rubocop
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 1.67.0
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 1.67.0
139
+ - !ruby/object:Gem::Dependency
140
+ name: rubocop-performance
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: 1.22.1
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: 1.22.1
153
+ - !ruby/object:Gem::Dependency
154
+ name: rubocop-rspec
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: 2.29.1
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: 2.29.1
167
+ - !ruby/object:Gem::Dependency
168
+ name: rubocop-sorbet
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: 0.8.5
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: 0.8.5
181
+ - !ruby/object:Gem::Dependency
182
+ name: simplecov
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - "~>"
186
+ - !ruby/object:Gem::Version
187
+ version: 0.22.0
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - "~>"
193
+ - !ruby/object:Gem::Version
194
+ version: 0.22.0
195
+ - !ruby/object:Gem::Dependency
196
+ name: turbo_tests
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - "~>"
200
+ - !ruby/object:Gem::Version
201
+ version: 2.2.0
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - "~>"
207
+ - !ruby/object:Gem::Version
208
+ version: 2.2.0
209
+ - !ruby/object:Gem::Dependency
210
+ name: vcr
211
+ requirement: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - "~>"
214
+ - !ruby/object:Gem::Version
215
+ version: '6.1'
216
+ type: :development
217
+ prerelease: false
218
+ version_requirements: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - "~>"
221
+ - !ruby/object:Gem::Version
222
+ version: '6.1'
223
+ - !ruby/object:Gem::Dependency
224
+ name: webmock
225
+ requirement: !ruby/object:Gem::Requirement
226
+ requirements:
227
+ - - "~>"
228
+ - !ruby/object:Gem::Version
229
+ version: '3.18'
230
+ type: :development
231
+ prerelease: false
232
+ version_requirements: !ruby/object:Gem::Requirement
233
+ requirements:
234
+ - - "~>"
235
+ - !ruby/object:Gem::Version
236
+ version: '3.18'
237
+ - !ruby/object:Gem::Dependency
238
+ name: webrick
239
+ requirement: !ruby/object:Gem::Requirement
240
+ requirements:
241
+ - - ">="
242
+ - !ruby/object:Gem::Version
243
+ version: '1.7'
244
+ type: :development
245
+ prerelease: false
246
+ version_requirements: !ruby/object:Gem::Requirement
247
+ requirements:
248
+ - - ">="
249
+ - !ruby/object:Gem::Version
250
+ version: '1.7'
251
+ description: Dependabot-bun provides support for bumping Javascript libraries using
252
+ bun via Dependabot. If you want support for multiple package managers, you probably
253
+ want the meta-gem dependabot-omnibus.
254
+ email: opensource@github.com
255
+ executables: []
256
+ extensions: []
257
+ extra_rdoc_files: []
258
+ files:
259
+ - lib/dependabot/bun.rb
260
+ - lib/dependabot/bun/file_fetcher.rb
261
+ - lib/dependabot/bun/file_parser/bun_lock.rb
262
+ - lib/dependabot/bun/helpers.rb
263
+ - lib/dependabot/bun/language.rb
264
+ - lib/dependabot/bun/package_manager.rb
265
+ - lib/dependabot/bun/requirement.rb
266
+ - lib/dependabot/bun/version.rb
267
+ - lib/dependabot/javascript.rb
268
+ - lib/dependabot/javascript/file_fetcher_helper.rb
269
+ - lib/dependabot/javascript/requirement.rb
270
+ - lib/dependabot/javascript/version.rb
271
+ homepage: https://github.com/dependabot/dependabot-core
272
+ licenses:
273
+ - MIT
274
+ metadata:
275
+ bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
276
+ changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.296.0
277
+ post_install_message:
278
+ rdoc_options: []
279
+ require_paths:
280
+ - lib
281
+ required_ruby_version: !ruby/object:Gem::Requirement
282
+ requirements:
283
+ - - ">="
284
+ - !ruby/object:Gem::Version
285
+ version: 3.1.0
286
+ required_rubygems_version: !ruby/object:Gem::Requirement
287
+ requirements:
288
+ - - ">="
289
+ - !ruby/object:Gem::Version
290
+ version: 3.1.0
291
+ requirements: []
292
+ rubygems_version: 3.5.22
293
+ signing_key:
294
+ specification_version: 4
295
+ summary: Provides Dependabot support for Bun
296
+ test_files: []