dependabot-bun 0.296.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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: []
|