dependabot-bun 0.296.0 → 0.296.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/dependabot/bun.rb +12 -2
- data/lib/dependabot/javascript/bun/file_fetcher.rb +77 -0
- data/lib/dependabot/javascript/bun/file_parser/bun_lock.rb +156 -0
- data/lib/dependabot/javascript/bun/file_parser/lockfile_parser.rb +55 -0
- data/lib/dependabot/javascript/bun/file_parser.rb +74 -0
- data/lib/dependabot/javascript/bun/file_updater/lockfile_updater.rb +138 -0
- data/lib/dependabot/javascript/bun/file_updater.rb +75 -0
- data/lib/dependabot/javascript/bun/helpers.rb +72 -0
- data/lib/dependabot/javascript/bun/package_manager.rb +48 -0
- data/lib/dependabot/javascript/bun/requirement.rb +11 -0
- data/lib/dependabot/javascript/bun/update_checker/conflicting_dependency_resolver.rb +64 -0
- data/lib/dependabot/javascript/bun/update_checker/dependency_files_builder.rb +47 -0
- data/lib/dependabot/javascript/bun/update_checker/latest_version_finder.rb +450 -0
- data/lib/dependabot/javascript/bun/update_checker/library_detector.rb +76 -0
- data/lib/dependabot/javascript/bun/update_checker/requirements_updater.rb +203 -0
- data/lib/dependabot/javascript/bun/update_checker/subdependency_version_resolver.rb +144 -0
- data/lib/dependabot/javascript/bun/update_checker/version_resolver.rb +525 -0
- data/lib/dependabot/javascript/bun/update_checker/vulnerability_auditor.rb +165 -0
- data/lib/dependabot/javascript/bun/update_checker.rb +440 -0
- data/lib/dependabot/javascript/bun/version.rb +11 -0
- data/lib/dependabot/javascript/shared/constraint_helper.rb +359 -0
- data/lib/dependabot/javascript/shared/dependency_files_filterer.rb +164 -0
- data/lib/dependabot/javascript/shared/file_fetcher.rb +283 -0
- data/lib/dependabot/javascript/shared/file_parser/lockfile_parser.rb +106 -0
- data/lib/dependabot/javascript/shared/file_parser.rb +454 -0
- data/lib/dependabot/javascript/shared/file_updater/npmrc_builder.rb +394 -0
- data/lib/dependabot/javascript/shared/file_updater/package_json_preparer.rb +87 -0
- data/lib/dependabot/javascript/shared/file_updater/package_json_updater.rb +376 -0
- data/lib/dependabot/javascript/shared/file_updater.rb +179 -0
- data/lib/dependabot/javascript/shared/language.rb +45 -0
- data/lib/dependabot/javascript/shared/metadata_finder.rb +209 -0
- data/lib/dependabot/javascript/shared/native_helpers.rb +21 -0
- data/lib/dependabot/javascript/shared/package_manager_detector.rb +72 -0
- data/lib/dependabot/javascript/shared/package_name.rb +118 -0
- data/lib/dependabot/javascript/shared/registry_helper.rb +190 -0
- data/lib/dependabot/javascript/shared/registry_parser.rb +93 -0
- data/lib/dependabot/javascript/shared/requirement.rb +144 -0
- data/lib/dependabot/javascript/shared/sub_dependency_files_filterer.rb +79 -0
- data/lib/dependabot/javascript/shared/update_checker/dependency_files_builder.rb +87 -0
- data/lib/dependabot/javascript/shared/update_checker/registry_finder.rb +358 -0
- data/lib/dependabot/javascript/shared/version.rb +133 -0
- data/lib/dependabot/javascript/shared/version_selector.rb +60 -0
- data/lib/dependabot/javascript.rb +31 -0
- metadata +48 -17
- data/lib/dependabot/bun/file_fetcher.rb +0 -97
- data/lib/dependabot/bun/file_parser/bun_lock.rb +0 -148
- data/lib/dependabot/bun/helpers.rb +0 -79
- data/lib/dependabot/bun/language.rb +0 -45
- data/lib/dependabot/bun/package_manager.rb +0 -46
- data/lib/dependabot/bun/requirement.rb +0 -14
- data/lib/dependabot/bun/version.rb +0 -12
- data/lib/dependabot/javascript/file_fetcher_helper.rb +0 -245
- data/lib/dependabot/javascript/requirement.rb +0 -141
- data/lib/dependabot/javascript/version.rb +0 -135
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0eb8192cb230095bd5c5c41ca47242ba70625b32e7e77eda267893b9984dc4ff
|
|
4
|
+
data.tar.gz: 4036b1f4381b20a6d797f5a7afa0d50d8dc1850acb10293835675132dceedbeb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c2559a11a91f31650bf5dc84a5d418c7280d55bab80b659986df71c9893cb9b494ccb8ec29f5bb1f9a3c154ee0b122919bc29021fc7ad1fb134e4d3c2230428d
|
|
7
|
+
data.tar.gz: 5433aab5f7fae6978db3f4d85ad18d0e91e37a725d9c2158b0b547247103b626e4fd41993f2be76d832dd2e7b89aeb975dd92bfc29b233e8db9be19a6d5a82c2
|
data/lib/dependabot/bun.rb
CHANGED
|
@@ -33,7 +33,17 @@ Dependabot::PullRequestCreator::Labeler
|
|
|
33
33
|
Dependabot::Dependency.register_production_check("bun", ->(_) { true })
|
|
34
34
|
|
|
35
35
|
module Dependabot
|
|
36
|
-
module
|
|
37
|
-
|
|
36
|
+
module Javascript
|
|
37
|
+
module Bun
|
|
38
|
+
ECOSYSTEM = "bun"
|
|
39
|
+
end
|
|
38
40
|
end
|
|
39
41
|
end
|
|
42
|
+
|
|
43
|
+
Dependabot::FileFetchers.register("bun", Dependabot::Javascript::Bun::FileFetcher)
|
|
44
|
+
Dependabot::FileParsers.register("bun", Dependabot::Javascript::Bun::FileParser)
|
|
45
|
+
Dependabot::FileUpdaters.register("bun", Dependabot::Javascript::Bun::FileUpdater)
|
|
46
|
+
Dependabot::UpdateCheckers.register("bun", Dependabot::Javascript::Bun::UpdateChecker)
|
|
47
|
+
Dependabot::MetadataFinders.register("bun", Dependabot::Javascript::Shared::MetadataFinder)
|
|
48
|
+
Dependabot::Utils.register_requirement_class("bun", Dependabot::Javascript::Bun::Requirement)
|
|
49
|
+
Dependabot::Utils.register_version_class("bun", Dependabot::Javascript::Bun::Version)
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# typed: strong
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Dependabot
|
|
5
|
+
module Javascript
|
|
6
|
+
module Bun
|
|
7
|
+
class FileFetcher < Shared::FileFetcher
|
|
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
|
+
private
|
|
44
|
+
|
|
45
|
+
sig { returns(T::Array[DependencyFile]) }
|
|
46
|
+
def bun_files
|
|
47
|
+
[bun_lock].compact
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
sig { returns(T.nilable(DependencyFile)) }
|
|
51
|
+
def bun_lock
|
|
52
|
+
return @bun_lock if defined?(@bun_lock)
|
|
53
|
+
|
|
54
|
+
@bun_lock ||= T.let(fetch_file_if_present(PackageManager::LOCKFILE_NAME), T.nilable(DependencyFile))
|
|
55
|
+
|
|
56
|
+
return @bun_lock if @bun_lock || directory == "/"
|
|
57
|
+
|
|
58
|
+
@bun_lock = fetch_file_from_parent_directories(self, PackageManager::LOCKFILE_NAME)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
sig { returns(T::Boolean) }
|
|
62
|
+
def ecosystem_enabled?
|
|
63
|
+
allow_beta_ecosystems? && Experiments.enabled?(:enable_bun_ecosystem)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
sig { returns(T::Hash[Symbol, String]) }
|
|
67
|
+
def unknown_ecosystem_versions
|
|
68
|
+
{
|
|
69
|
+
package_managers: {
|
|
70
|
+
"unknown" => 0
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Dependabot
|
|
5
|
+
module Javascript
|
|
6
|
+
module Bun
|
|
7
|
+
class FileParser
|
|
8
|
+
class BunLock < Shared::FileParser::Lockfile
|
|
9
|
+
extend T::Sig
|
|
10
|
+
|
|
11
|
+
sig { params(dependency_file: DependencyFile).void }
|
|
12
|
+
def initialize(dependency_file)
|
|
13
|
+
super
|
|
14
|
+
@parsed = T.let(
|
|
15
|
+
nil,
|
|
16
|
+
T.nilable(T::Hash[String, T.any(Integer, String, T::Array[String], T::Hash[String, String])])
|
|
17
|
+
)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
sig { override.returns(T::Hash[String, T.untyped]) }
|
|
21
|
+
def parsed
|
|
22
|
+
@parsed ||= begin
|
|
23
|
+
content = begin
|
|
24
|
+
# Since bun.lock is a JSONC file, which is a subset of YAML, we can use YAML to parse it
|
|
25
|
+
YAML.load(T.must(@dependency_file.content))
|
|
26
|
+
rescue Psych::SyntaxError => e
|
|
27
|
+
raise_invalid!("malformed JSONC at line #{e.line}, column #{e.column}")
|
|
28
|
+
end
|
|
29
|
+
raise_invalid!("expected to be an object") unless content.is_a?(Hash)
|
|
30
|
+
|
|
31
|
+
version = content["lockfileVersion"]
|
|
32
|
+
raise_invalid!("expected 'lockfileVersion' to be an integer") unless version.is_a?(Integer)
|
|
33
|
+
raise_invalid!("expected 'lockfileVersion' to be >= 0") unless version >= 0
|
|
34
|
+
|
|
35
|
+
content
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
sig { override.returns(Dependabot::FileParsers::Base::DependencySet) }
|
|
40
|
+
def dependencies
|
|
41
|
+
dependency_set = Dependabot::FileParsers::Base::DependencySet.new
|
|
42
|
+
|
|
43
|
+
# bun.lock v0 format:
|
|
44
|
+
# https://github.com/oven-sh/bun/blob/c130df6c589fdf28f9f3c7f23ed9901140bc9349/src/install/bun.lock.zig#L595-L605
|
|
45
|
+
|
|
46
|
+
packages = parsed["packages"]
|
|
47
|
+
raise_invalid!("expected 'packages' to be an object") unless packages.is_a?(Hash)
|
|
48
|
+
|
|
49
|
+
packages.each do |key, details|
|
|
50
|
+
raise_invalid!("expected 'packages.#{key}' to be an array") unless details.is_a?(Array)
|
|
51
|
+
|
|
52
|
+
resolution = details.first
|
|
53
|
+
raise_invalid!("expected 'packages.#{key}[0]' to be a string") unless resolution.is_a?(String)
|
|
54
|
+
|
|
55
|
+
name, version = resolution.split(/(?<=\w)\@/)
|
|
56
|
+
next if name.empty?
|
|
57
|
+
|
|
58
|
+
semver = Version.semver_for(version)
|
|
59
|
+
next unless semver
|
|
60
|
+
|
|
61
|
+
dependency_set << Dependency.new(
|
|
62
|
+
name: name,
|
|
63
|
+
version: semver.to_s,
|
|
64
|
+
package_manager: "bun",
|
|
65
|
+
requirements: []
|
|
66
|
+
)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
dependency_set
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
sig do
|
|
73
|
+
override
|
|
74
|
+
.params(
|
|
75
|
+
dependency_name: String,
|
|
76
|
+
requirement: T.untyped,
|
|
77
|
+
manifest_name: String
|
|
78
|
+
)
|
|
79
|
+
.returns(T.nilable(T::Hash[String, T.untyped]))
|
|
80
|
+
end
|
|
81
|
+
def details(dependency_name, requirement, manifest_name) # rubocop:disable Lint/UnusedMethodArgument
|
|
82
|
+
packages = parsed["packages"]
|
|
83
|
+
return unless packages.is_a?(Hash)
|
|
84
|
+
|
|
85
|
+
candidates =
|
|
86
|
+
packages
|
|
87
|
+
.select { |name, _| name == dependency_name }
|
|
88
|
+
.values
|
|
89
|
+
|
|
90
|
+
# If there's only one entry for this dependency, use it, even if
|
|
91
|
+
# the requirement in the lockfile doesn't match
|
|
92
|
+
if candidates.one?
|
|
93
|
+
parse_details(candidates.first)
|
|
94
|
+
else
|
|
95
|
+
candidate = candidates.find do |label, _|
|
|
96
|
+
label.scan(/(?<=\w)\@(?:npm:)?([^\s,]+)/).flatten.include?(requirement)
|
|
97
|
+
end&.last
|
|
98
|
+
parse_details(candidate)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
private
|
|
103
|
+
|
|
104
|
+
sig { params(message: String).void }
|
|
105
|
+
def raise_invalid!(message)
|
|
106
|
+
raise Dependabot::DependencyFileNotParseable.new(@dependency_file.path, "Invalid bun.lock file: #{message}")
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
sig do
|
|
110
|
+
params(entry: T.nilable(T::Array[T.untyped])).returns(T.nilable(T::Hash[String, T.untyped]))
|
|
111
|
+
end
|
|
112
|
+
def parse_details(entry)
|
|
113
|
+
return unless entry.is_a?(Array)
|
|
114
|
+
|
|
115
|
+
# Either:
|
|
116
|
+
# - "{name}@{version}", registry, details, integrity
|
|
117
|
+
# - "{name}@{resolution}", details
|
|
118
|
+
resolution = entry.first
|
|
119
|
+
return unless resolution.is_a?(String)
|
|
120
|
+
|
|
121
|
+
name, version = resolution.split(/(?<=\w)\@/)
|
|
122
|
+
semver = Version.semver_for(version)
|
|
123
|
+
|
|
124
|
+
if semver
|
|
125
|
+
registry, details, integrity = entry[1..3]
|
|
126
|
+
{
|
|
127
|
+
"name" => name,
|
|
128
|
+
"version" => semver.to_s,
|
|
129
|
+
"registry" => registry,
|
|
130
|
+
"details" => details,
|
|
131
|
+
"integrity" => integrity
|
|
132
|
+
}
|
|
133
|
+
else
|
|
134
|
+
details = entry[1]
|
|
135
|
+
{
|
|
136
|
+
"name" => name,
|
|
137
|
+
"resolution" => version,
|
|
138
|
+
"details" => details
|
|
139
|
+
}
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
sig { override.returns(T::Array[Dependabot::Dependency]) }
|
|
145
|
+
def parse
|
|
146
|
+
[]
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
private
|
|
150
|
+
|
|
151
|
+
sig { override.void }
|
|
152
|
+
def check_required_files; end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Dependabot
|
|
5
|
+
module Javascript
|
|
6
|
+
module Bun
|
|
7
|
+
class FileParser
|
|
8
|
+
class LockfileParser < Shared::FileParser::LockfileParser
|
|
9
|
+
extend T::Sig
|
|
10
|
+
|
|
11
|
+
DEFAULT_LOCKFILES = %w(bun.lock).freeze
|
|
12
|
+
|
|
13
|
+
sig { override.returns(Dependabot::FileParsers::Base::DependencySet) }
|
|
14
|
+
def parse_set
|
|
15
|
+
dependency_set = Dependabot::FileParsers::Base::DependencySet.new
|
|
16
|
+
|
|
17
|
+
bun_locks.each do |file|
|
|
18
|
+
dependency_set += lockfile_for(file).dependencies
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
dependency_set
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
sig { override.returns(T::Array[String]) }
|
|
25
|
+
def default_lockfiles
|
|
26
|
+
DEFAULT_LOCKFILES
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
sig { override.params(file: DependencyFile).returns(BunLock) }
|
|
32
|
+
def lockfile_for(file)
|
|
33
|
+
@lockfiles ||= T.let({}, T.nilable(T::Hash[String, BunLock]))
|
|
34
|
+
@lockfiles[file.name] ||= case file.name
|
|
35
|
+
when *bun_locks.map(&:name)
|
|
36
|
+
Bun::FileParser::BunLock.new(file)
|
|
37
|
+
else
|
|
38
|
+
raise "Unexpected lockfile: #{file.name}"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
sig { returns(T::Array[DependencyFile]) }
|
|
43
|
+
def bun_locks
|
|
44
|
+
@bun_locks ||= T.let(select_files_by_extension("bun.lock"), T.nilable(T::Array[DependencyFile]))
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
sig { override.returns(T.class_of(Version)) }
|
|
48
|
+
def version_class
|
|
49
|
+
Version
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# typed: strong
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# See https://docs.npmjs.com/files/package.json for package.json format docs.
|
|
5
|
+
|
|
6
|
+
module Dependabot
|
|
7
|
+
module Javascript
|
|
8
|
+
module Bun
|
|
9
|
+
class FileParser < Shared::FileParser
|
|
10
|
+
extend T::Sig
|
|
11
|
+
|
|
12
|
+
sig { override.returns(Ecosystem) }
|
|
13
|
+
def ecosystem
|
|
14
|
+
@ecosystem ||= T.let(
|
|
15
|
+
Ecosystem.new(
|
|
16
|
+
name: ECOSYSTEM,
|
|
17
|
+
package_manager: PackageManager.new(detected_version:),
|
|
18
|
+
language: Shared::Language.new(detected_version:)
|
|
19
|
+
),
|
|
20
|
+
T.nilable(Ecosystem)
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
sig { returns(T.nilable(String)) }
|
|
27
|
+
def detected_version
|
|
28
|
+
Helpers.local_package_manager_version(Bun::PackageManager::NAME)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
sig { override.returns(T::Hash[Symbol, T.nilable(Dependabot::DependencyFile)]) }
|
|
32
|
+
def lockfiles
|
|
33
|
+
{
|
|
34
|
+
bun: bun_lock
|
|
35
|
+
}
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
sig { override.returns(LockfileParser) }
|
|
39
|
+
def lockfile_parser
|
|
40
|
+
@lockfile_parser ||= T.let(LockfileParser.new(
|
|
41
|
+
dependency_files: dependency_files
|
|
42
|
+
), T.nilable(LockfileParser))
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
sig { override.returns(T::Hash[Symbol, T.nilable(Dependabot::DependencyFile)]) }
|
|
46
|
+
def registry_config_files
|
|
47
|
+
{
|
|
48
|
+
npmrc: npmrc
|
|
49
|
+
}
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
|
53
|
+
def bun_lock
|
|
54
|
+
@bun_lock ||= T.let(dependency_files.find do |f|
|
|
55
|
+
f.name.end_with?(PackageManager::LOCKFILE_NAME)
|
|
56
|
+
end, T.nilable(Dependabot::DependencyFile))
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
sig { override.returns(T.class_of(Version)) }
|
|
60
|
+
def version_class
|
|
61
|
+
Version
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
sig { override.returns(T.class_of(Requirement)) }
|
|
65
|
+
def requirement_class
|
|
66
|
+
Requirement
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
sig { override.void }
|
|
70
|
+
def check_required_files; end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Dependabot
|
|
5
|
+
module Javascript
|
|
6
|
+
module Bun
|
|
7
|
+
class FileUpdater
|
|
8
|
+
class LockfileUpdater
|
|
9
|
+
def initialize(dependencies:, dependency_files:, repo_contents_path:, credentials:)
|
|
10
|
+
@dependencies = dependencies
|
|
11
|
+
@dependency_files = dependency_files
|
|
12
|
+
@repo_contents_path = repo_contents_path
|
|
13
|
+
@credentials = credentials
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def updated_bun_lock_content(bun_lock)
|
|
17
|
+
@updated_bun_lock_content ||= {}
|
|
18
|
+
return @updated_bun_lock_content[bun_lock.name] if @updated_bun_lock_content[bun_lock.name]
|
|
19
|
+
|
|
20
|
+
new_content = run_bun_update(bun_lock: bun_lock)
|
|
21
|
+
@updated_bun_lock_content[bun_lock.name] = new_content
|
|
22
|
+
rescue SharedHelpers::HelperSubprocessFailed => e
|
|
23
|
+
handle_bun_lock_updater_error(e, bun_lock)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
attr_reader :dependencies
|
|
29
|
+
attr_reader :dependency_files
|
|
30
|
+
attr_reader :repo_contents_path
|
|
31
|
+
attr_reader :credentials
|
|
32
|
+
|
|
33
|
+
ERR_PATTERNS = {
|
|
34
|
+
/get .* 404/i => Dependabot::DependencyNotFound,
|
|
35
|
+
/installfailed cloning repository/i => Dependabot::DependencyNotFound,
|
|
36
|
+
/file:.* failed to resolve/i => Dependabot::DependencyNotFound,
|
|
37
|
+
/no version matching/i => Dependabot::DependencyFileNotResolvable,
|
|
38
|
+
/failed to resolve/i => Dependabot::DependencyFileNotResolvable
|
|
39
|
+
}.freeze
|
|
40
|
+
|
|
41
|
+
def run_bun_update(bun_lock:)
|
|
42
|
+
SharedHelpers.in_a_temporary_repo_directory(base_dir, repo_contents_path) do
|
|
43
|
+
File.write(".npmrc", npmrc_content(bun_lock))
|
|
44
|
+
|
|
45
|
+
SharedHelpers.with_git_configured(credentials: credentials) do
|
|
46
|
+
run_bun_updater
|
|
47
|
+
|
|
48
|
+
write_final_package_json_files
|
|
49
|
+
|
|
50
|
+
run_bun_install
|
|
51
|
+
|
|
52
|
+
File.read(bun_lock.name)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def run_bun_updater
|
|
58
|
+
dependency_updates = dependencies.map do |d|
|
|
59
|
+
"#{d.name}@#{d.version}"
|
|
60
|
+
end.join(" ")
|
|
61
|
+
|
|
62
|
+
Helpers.run_bun_command(
|
|
63
|
+
"install #{dependency_updates} --save-text-lockfile",
|
|
64
|
+
fingerprint: "install <dependency_updates> --save-text-lockfile"
|
|
65
|
+
)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def run_bun_install
|
|
69
|
+
Helpers.run_bun_command(
|
|
70
|
+
"install --save-text-lockfile"
|
|
71
|
+
)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def lockfile_dependencies(lockfile)
|
|
75
|
+
@lockfile_dependencies ||= {}
|
|
76
|
+
@lockfile_dependencies[lockfile.name] ||=
|
|
77
|
+
FileParser.new(
|
|
78
|
+
dependency_files: [lockfile, *package_files],
|
|
79
|
+
source: nil,
|
|
80
|
+
credentials: credentials
|
|
81
|
+
).parse
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def handle_bun_lock_updater_error(error, _bun_lock)
|
|
85
|
+
error_message = error.message
|
|
86
|
+
|
|
87
|
+
ERR_PATTERNS.each do |pattern, error_class|
|
|
88
|
+
raise error_class, error_message if error_message.match?(pattern)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
raise error
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def write_final_package_json_files
|
|
95
|
+
package_files.each do |file|
|
|
96
|
+
path = file.name
|
|
97
|
+
FileUtils.mkdir_p(Pathname.new(path).dirname)
|
|
98
|
+
File.write(path, updated_package_json_content(file))
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def npmrc_content(bun_lock)
|
|
103
|
+
Dependabot::Javascript::Shared::FileUpdater::NpmrcBuilder.new(
|
|
104
|
+
credentials: credentials,
|
|
105
|
+
dependency_files: dependency_files,
|
|
106
|
+
dependencies: lockfile_dependencies(bun_lock)
|
|
107
|
+
).npmrc_content
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def updated_package_json_content(file)
|
|
111
|
+
@updated_package_json_content ||= {}
|
|
112
|
+
@updated_package_json_content[file.name] ||=
|
|
113
|
+
Dependabot::Javascript::Shared::FileUpdater::PackageJsonUpdater.new(
|
|
114
|
+
package_json: file,
|
|
115
|
+
dependencies: dependencies
|
|
116
|
+
).updated_package_json.content
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def package_files
|
|
120
|
+
@package_files ||= dependency_files.select { |f| f.name.end_with?("package.json") }
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def base_dir
|
|
124
|
+
dependency_files.first.directory
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def npmrc_file
|
|
128
|
+
dependency_files.find { |f| f.name == ".npmrc" }
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def sanitize_message(message)
|
|
132
|
+
message.gsub(/"|\[|\]|\}|\{/, "")
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Dependabot
|
|
5
|
+
module Javascript
|
|
6
|
+
module Bun
|
|
7
|
+
class FileUpdater < Shared::FileUpdater
|
|
8
|
+
sig { override.returns(T::Array[Regexp]) }
|
|
9
|
+
def self.updated_files_regex
|
|
10
|
+
[
|
|
11
|
+
%r{^(?:.*/)?package\.json$},
|
|
12
|
+
%r{^(?:.*/)?bun\.lock$} # Matches bun.lock files
|
|
13
|
+
]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
sig { override.returns(T.class_of(FileParser::LockfileParser)) }
|
|
19
|
+
def lockfile_parser_class
|
|
20
|
+
FileParser::LockfileParser
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
|
24
|
+
def bun_locks
|
|
25
|
+
@bun_locks ||= T.let(
|
|
26
|
+
filtered_dependency_files
|
|
27
|
+
.select { |f| f.name.end_with?("bun.lock") },
|
|
28
|
+
T.nilable(T::Array[Dependabot::DependencyFile])
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
sig { params(bun_lock: Dependabot::DependencyFile).returns(T::Boolean) }
|
|
33
|
+
def bun_lock_changed?(bun_lock)
|
|
34
|
+
bun_lock.content != updated_bun_lock_content(bun_lock)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
sig { override.returns(T::Array[Dependabot::DependencyFile]) }
|
|
38
|
+
def updated_lockfiles
|
|
39
|
+
updated_files = []
|
|
40
|
+
|
|
41
|
+
bun_locks.each do |bun_lock|
|
|
42
|
+
next unless bun_lock_changed?(bun_lock)
|
|
43
|
+
|
|
44
|
+
updated_files << updated_file(
|
|
45
|
+
file: bun_lock,
|
|
46
|
+
content: updated_bun_lock_content(bun_lock)
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
updated_files
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
sig { params(bun_lock: Dependabot::DependencyFile).returns(String) }
|
|
54
|
+
def updated_bun_lock_content(bun_lock)
|
|
55
|
+
@updated_bun_lock_content ||= T.let({}, T.nilable(T::Hash[String, T.nilable(String)]))
|
|
56
|
+
@updated_bun_lock_content[bun_lock.name] ||=
|
|
57
|
+
bun_lockfile_updater.updated_bun_lock_content(bun_lock)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
sig { returns(Bun::FileUpdater::LockfileUpdater) }
|
|
61
|
+
def bun_lockfile_updater
|
|
62
|
+
@bun_lockfile_updater ||= T.let(
|
|
63
|
+
LockfileUpdater.new(
|
|
64
|
+
dependencies: dependencies,
|
|
65
|
+
dependency_files: dependency_files,
|
|
66
|
+
repo_contents_path: repo_contents_path,
|
|
67
|
+
credentials: credentials
|
|
68
|
+
),
|
|
69
|
+
T.nilable(Bun::FileUpdater::LockfileUpdater)
|
|
70
|
+
)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# typed: strong
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Dependabot
|
|
5
|
+
module Javascript
|
|
6
|
+
module Bun
|
|
7
|
+
module Helpers
|
|
8
|
+
extend T::Sig
|
|
9
|
+
|
|
10
|
+
# BUN Version Constants
|
|
11
|
+
BUN_V1 = 1
|
|
12
|
+
BUN_DEFAULT_VERSION = BUN_V1
|
|
13
|
+
|
|
14
|
+
sig { params(_bun_lock: T.nilable(DependencyFile)).returns(Integer) }
|
|
15
|
+
def self.bun_version_numeric(_bun_lock)
|
|
16
|
+
BUN_DEFAULT_VERSION
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
sig { returns(T.nilable(String)) }
|
|
20
|
+
def self.bun_version
|
|
21
|
+
run_bun_command("--version", fingerprint: "--version").strip
|
|
22
|
+
rescue StandardError => e
|
|
23
|
+
Dependabot.logger.error("Error retrieving Bun version: #{e.message}")
|
|
24
|
+
nil
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
sig { params(command: String, fingerprint: T.nilable(String)).returns(String) }
|
|
28
|
+
def self.run_bun_command(command, fingerprint: nil)
|
|
29
|
+
full_command = "bun #{command}"
|
|
30
|
+
|
|
31
|
+
Dependabot.logger.info("Running bun command: #{full_command}")
|
|
32
|
+
|
|
33
|
+
result = Dependabot::SharedHelpers.run_shell_command(
|
|
34
|
+
full_command,
|
|
35
|
+
fingerprint: "bun #{fingerprint || command}"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
Dependabot.logger.info("Command executed successfully: #{full_command}")
|
|
39
|
+
result
|
|
40
|
+
rescue StandardError => e
|
|
41
|
+
Dependabot.logger.error("Error running bun command: #{full_command}, Error: #{e.message}")
|
|
42
|
+
raise
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Fetch the currently installed version of the package manager directly
|
|
46
|
+
# from the system
|
|
47
|
+
sig { params(name: String).returns(String) }
|
|
48
|
+
def self.local_package_manager_version(name)
|
|
49
|
+
Dependabot::SharedHelpers.run_shell_command(
|
|
50
|
+
"#{name} -v",
|
|
51
|
+
fingerprint: "#{name} -v"
|
|
52
|
+
).strip
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Run single command on package manager returning stdout/stderr
|
|
56
|
+
sig do
|
|
57
|
+
params(
|
|
58
|
+
name: String,
|
|
59
|
+
command: String,
|
|
60
|
+
fingerprint: T.nilable(String)
|
|
61
|
+
).returns(String)
|
|
62
|
+
end
|
|
63
|
+
def self.package_manager_run_command(name, command, fingerprint: nil)
|
|
64
|
+
return run_bun_command(command, fingerprint: fingerprint) if name == PackageManager::NAME
|
|
65
|
+
|
|
66
|
+
# TODO: remove this method and just use the one in the PackageManager class
|
|
67
|
+
"noop"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|