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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/lib/dependabot/bun.rb +12 -2
  3. data/lib/dependabot/javascript/bun/file_fetcher.rb +77 -0
  4. data/lib/dependabot/javascript/bun/file_parser/bun_lock.rb +156 -0
  5. data/lib/dependabot/javascript/bun/file_parser/lockfile_parser.rb +55 -0
  6. data/lib/dependabot/javascript/bun/file_parser.rb +74 -0
  7. data/lib/dependabot/javascript/bun/file_updater/lockfile_updater.rb +138 -0
  8. data/lib/dependabot/javascript/bun/file_updater.rb +75 -0
  9. data/lib/dependabot/javascript/bun/helpers.rb +72 -0
  10. data/lib/dependabot/javascript/bun/package_manager.rb +48 -0
  11. data/lib/dependabot/javascript/bun/requirement.rb +11 -0
  12. data/lib/dependabot/javascript/bun/update_checker/conflicting_dependency_resolver.rb +64 -0
  13. data/lib/dependabot/javascript/bun/update_checker/dependency_files_builder.rb +47 -0
  14. data/lib/dependabot/javascript/bun/update_checker/latest_version_finder.rb +450 -0
  15. data/lib/dependabot/javascript/bun/update_checker/library_detector.rb +76 -0
  16. data/lib/dependabot/javascript/bun/update_checker/requirements_updater.rb +203 -0
  17. data/lib/dependabot/javascript/bun/update_checker/subdependency_version_resolver.rb +144 -0
  18. data/lib/dependabot/javascript/bun/update_checker/version_resolver.rb +525 -0
  19. data/lib/dependabot/javascript/bun/update_checker/vulnerability_auditor.rb +165 -0
  20. data/lib/dependabot/javascript/bun/update_checker.rb +440 -0
  21. data/lib/dependabot/javascript/bun/version.rb +11 -0
  22. data/lib/dependabot/javascript/shared/constraint_helper.rb +359 -0
  23. data/lib/dependabot/javascript/shared/dependency_files_filterer.rb +164 -0
  24. data/lib/dependabot/javascript/shared/file_fetcher.rb +283 -0
  25. data/lib/dependabot/javascript/shared/file_parser/lockfile_parser.rb +106 -0
  26. data/lib/dependabot/javascript/shared/file_parser.rb +454 -0
  27. data/lib/dependabot/javascript/shared/file_updater/npmrc_builder.rb +394 -0
  28. data/lib/dependabot/javascript/shared/file_updater/package_json_preparer.rb +87 -0
  29. data/lib/dependabot/javascript/shared/file_updater/package_json_updater.rb +376 -0
  30. data/lib/dependabot/javascript/shared/file_updater.rb +179 -0
  31. data/lib/dependabot/javascript/shared/language.rb +45 -0
  32. data/lib/dependabot/javascript/shared/metadata_finder.rb +209 -0
  33. data/lib/dependabot/javascript/shared/native_helpers.rb +21 -0
  34. data/lib/dependabot/javascript/shared/package_manager_detector.rb +72 -0
  35. data/lib/dependabot/javascript/shared/package_name.rb +118 -0
  36. data/lib/dependabot/javascript/shared/registry_helper.rb +190 -0
  37. data/lib/dependabot/javascript/shared/registry_parser.rb +93 -0
  38. data/lib/dependabot/javascript/shared/requirement.rb +144 -0
  39. data/lib/dependabot/javascript/shared/sub_dependency_files_filterer.rb +79 -0
  40. data/lib/dependabot/javascript/shared/update_checker/dependency_files_builder.rb +87 -0
  41. data/lib/dependabot/javascript/shared/update_checker/registry_finder.rb +358 -0
  42. data/lib/dependabot/javascript/shared/version.rb +133 -0
  43. data/lib/dependabot/javascript/shared/version_selector.rb +60 -0
  44. data/lib/dependabot/javascript.rb +31 -0
  45. metadata +48 -17
  46. data/lib/dependabot/bun/file_fetcher.rb +0 -97
  47. data/lib/dependabot/bun/file_parser/bun_lock.rb +0 -148
  48. data/lib/dependabot/bun/helpers.rb +0 -79
  49. data/lib/dependabot/bun/language.rb +0 -45
  50. data/lib/dependabot/bun/package_manager.rb +0 -46
  51. data/lib/dependabot/bun/requirement.rb +0 -14
  52. data/lib/dependabot/bun/version.rb +0 -12
  53. data/lib/dependabot/javascript/file_fetcher_helper.rb +0 -245
  54. data/lib/dependabot/javascript/requirement.rb +0 -141
  55. data/lib/dependabot/javascript/version.rb +0 -135
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aec5d70543afebc798276a80ab116a1375a90395a66815467b48ffc778f1287c
4
- data.tar.gz: f44ddcdc48fee485db9e56d97c06040e9bcf597b42b057be8a98ca4eadbc7665
3
+ metadata.gz: 0eb8192cb230095bd5c5c41ca47242ba70625b32e7e77eda267893b9984dc4ff
4
+ data.tar.gz: 4036b1f4381b20a6d797f5a7afa0d50d8dc1850acb10293835675132dceedbeb
5
5
  SHA512:
6
- metadata.gz: ad382c4264db5281a25cd912a5379316e175efef2c4188e89d05699eeffc753d6114fd9046ba7fafe5bf979fd83c7ad5ec7cc2bec3237aa477d42bb19dd3b7c8
7
- data.tar.gz: fd61ab1f24b72c1306b2b187728cfc45581740515d7000a6026bde6c5313b3e7bf3e05af3ce0e77a3d0af530e4a09a3f5b26ab518c577ef4fef5176431e44fe3
6
+ metadata.gz: c2559a11a91f31650bf5dc84a5d418c7280d55bab80b659986df71c9893cb9b494ccb8ec29f5bb1f9a3c154ee0b122919bc29021fc7ad1fb134e4d3c2230428d
7
+ data.tar.gz: 5433aab5f7fae6978db3f4d85ad18d0e91e37a725d9c2158b0b547247103b626e4fd41993f2be76d832dd2e7b89aeb975dd92bfc29b233e8db9be19a6d5a82c2
@@ -33,7 +33,17 @@ Dependabot::PullRequestCreator::Labeler
33
33
  Dependabot::Dependency.register_production_check("bun", ->(_) { true })
34
34
 
35
35
  module Dependabot
36
- module Bun
37
- ECOSYSTEM = "bun"
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