dependabot-bun 0.296.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/dependabot/bun/file_fetcher.rb +97 -0
- data/lib/dependabot/bun/file_parser/bun_lock.rb +148 -0
- data/lib/dependabot/bun/helpers.rb +79 -0
- data/lib/dependabot/bun/language.rb +45 -0
- data/lib/dependabot/bun/package_manager.rb +46 -0
- data/lib/dependabot/bun/requirement.rb +14 -0
- data/lib/dependabot/bun/version.rb +12 -0
- data/lib/dependabot/bun.rb +39 -0
- data/lib/dependabot/javascript/file_fetcher_helper.rb +245 -0
- data/lib/dependabot/javascript/requirement.rb +141 -0
- data/lib/dependabot/javascript/version.rb +135 -0
- data/lib/dependabot/javascript.rb +8 -0
- metadata +296 -0
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,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
|
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: []
|