Skip to content

Commit

Permalink
Merge pull request #11287 from dependabot/add-bun-file-fetcher
Browse files Browse the repository at this point in the history
Add Bun file FileFetcher
  • Loading branch information
markhallen authored Jan 14, 2025
2 parents c4fa116 + 800dbd5 commit 492491c
Show file tree
Hide file tree
Showing 8 changed files with 218 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def package_required_lockfile?(lockfile)

sig { params(lockfile: DependencyFile).returns(T::Boolean) }
def workspaces_lockfile?(lockfile)
return false unless ["yarn.lock", "package-lock.json", "pnpm-lock.yaml"].include?(lockfile.name)
return false unless ["yarn.lock", "package-lock.json", "pnpm-lock.yaml", "bun.lock"].include?(lockfile.name)

return false unless parsed_root_package_json["workspaces"] || dependency_files.any? do |file|
file.name.end_with?("pnpm-workspace.yaml") && File.dirname(file.name) == File.dirname(lockfile.name)
Expand Down Expand Up @@ -148,6 +148,7 @@ def lockfile?(file)
"package-lock.json",
"yarn.lock",
"pnpm-lock.yaml",
"bun.lock",
"npm-shrinkwrap.json"
)
end
Expand Down
83 changes: 50 additions & 33 deletions npm_and_yarn/lib/dependabot/npm_and_yarn/file_fetcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ def ecosystem_versions
package_managers["npm"] = npm_version if npm_version
package_managers["yarn"] = yarn_version if yarn_version
package_managers["pnpm"] = pnpm_version if pnpm_version
package_managers["bun"] = bun_version if bun_version
package_managers["unknown"] = 1 if package_managers.empty?

{
Expand All @@ -83,6 +84,7 @@ def fetch_files
fetched_files += npm_files if npm_version
fetched_files += yarn_files if yarn_version
fetched_files += pnpm_files if pnpm_version
fetched_files += bun_files if bun_version
fetched_files += lerna_files
fetched_files += workspace_package_jsons
fetched_files += path_dependencies(fetched_files)
Expand Down Expand Up @@ -120,6 +122,13 @@ def pnpm_files
fetched_pnpm_files
end

sig { returns(T::Array[DependencyFile]) }
def bun_files
fetched_bun_files = []
fetched_bun_files << bun_lock if bun_lock
fetched_bun_files
end

sig { returns(T::Array[DependencyFile]) }
def lerna_files
fetched_lerna_files = []
Expand Down Expand Up @@ -202,6 +211,16 @@ def pnpm_version
)
end

sig { returns(T.nilable(T.any(Integer, String))) }
def bun_version
return @bun_version = nil unless Experiments.enabled?(:bun_updates)

@bun_version ||= T.let(
package_manager_helper.setup(BunPackageManager::NAME),
T.nilable(T.any(Integer, String))
)
end

sig { returns(PackageManagerHelper) }
def package_manager_helper
@package_manager_helper ||= T.let(
Expand All @@ -219,7 +238,8 @@ def lockfiles
{
npm: package_lock || shrinkwrap,
yarn: yarn_lock,
pnpm: pnpm_lock
pnpm: pnpm_lock,
bun: bun_lock
}
end

Expand Down Expand Up @@ -261,17 +281,18 @@ def pnpm_lock

return @pnpm_lock if @pnpm_lock || directory == "/"

# Loop through parent directories looking for a pnpm-lock
(1..directory.split("/").count).each do |i|
@pnpm_lock = fetch_file_from_host(("../" * i) + PNPMPackageManager::LOCKFILE_NAME)
.tap { |f| f.support_file = true }
break if @pnpm_lock
rescue Dependabot::DependencyFileNotFound
# Ignore errors (pnpm_lock.yaml may not be present)
nil
end
@pnpm_lock = fetch_file_from_parent_directories(PNPMPackageManager::LOCKFILE_NAME)
end

sig { returns(T.nilable(DependencyFile)) }
def bun_lock
return @bun_lock if defined?(@bun_lock)

@bun_lock ||= T.let(fetch_file_if_present(BunPackageManager::LOCKFILE_NAME), T.nilable(DependencyFile))

@pnpm_lock
return @bun_lock if @bun_lock || directory == "/"

@bun_lock = fetch_file_from_parent_directories(BunPackageManager::LOCKFILE_NAME)
end

sig { returns(T.nilable(DependencyFile)) }
Expand All @@ -294,17 +315,7 @@ def npmrc

return @npmrc if @npmrc || directory == "/"

# Loop through parent directories looking for an npmrc
(1..directory.split("/").count).each do |i|
@npmrc = fetch_file_from_host(("../" * i) + NpmPackageManager::RC_FILENAME)
.tap { |f| f.support_file = true }
break if @npmrc
rescue Dependabot::DependencyFileNotFound
# Ignore errors (.npmrc may not be present)
nil
end

@npmrc
@npmrc = fetch_file_from_parent_directories(NpmPackageManager::RC_FILENAME)
end

sig { returns(T.nilable(DependencyFile)) }
Expand All @@ -315,17 +326,7 @@ def yarnrc

return @yarnrc if @yarnrc || directory == "/"

# Loop through parent directories looking for an yarnrc
(1..directory.split("/").count).each do |i|
@yarnrc = fetch_file_from_host(("../" * i) + YarnPackageManager::RC_FILENAME)
.tap { |f| f.support_file = true }
break if @yarnrc
rescue Dependabot::DependencyFileNotFound
# Ignore errors (.yarnrc may not be present)
nil
end

@yarnrc
@yarnrc = fetch_file_from_parent_directories(YarnPackageManager::RC_FILENAME)
end

sig { returns(T.nilable(DependencyFile)) }
Expand Down Expand Up @@ -699,6 +700,22 @@ def create_yarn_cache
Dependabot.logger.info("Repository contents path does not exist")
end
end

sig { params(filename: String).returns(T.nilable(DependencyFile)) }
def fetch_file_with_support(filename)
fetch_file_from_host(filename).tap { |f| f.support_file = true }
rescue Dependabot::DependencyFileNotFound
nil
end

sig { params(filename: String).returns(T.nilable(DependencyFile)) }
def fetch_file_from_parent_directories(filename)
(1..directory.split("/").count).each do |i|
file = fetch_file_with_support(("../" * i) + filename)
return file if file
end
nil
end
end
end
end
Expand Down
9 changes: 9 additions & 0 deletions npm_and_yarn/lib/dependabot/npm_and_yarn/helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ module Helpers # rubocop:disable Metrics/ModuleLength
PNPM_DEFAULT_VERSION = PNPM_V9
PNPM_FALLBACK_VERSION = PNPM_V6

# BUN Version Constants
BUN_V1 = 1
BUN_DEFAULT_VERSION = BUN_V1

# YARN Version Constants
YARN_V3 = 3
YARN_V2 = 2
Expand Down Expand Up @@ -159,6 +163,11 @@ def self.pnpm_version_numeric(pnpm_lock)
PNPM_FALLBACK_VERSION
end

sig { params(_bun_lock: T.nilable(DependencyFile)).returns(Integer) }
def self.bun_version_numeric(_bun_lock)
BUN_DEFAULT_VERSION
end

sig { params(key: String, default_value: String).returns(T.untyped) }
def self.fetch_yarnrc_yml_value(key, default_value)
if File.exist?(".yarnrc.yml") && (yarnrc = YAML.load_file(".yarnrc.yml"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ def lockfile?(file)
"package-lock.json",
"yarn.lock",
"npm-shrinkwrap.json",
"bun.lock",
"pnpm-lock.yaml"
)
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,17 @@ def project_dependency_file(file_name)
end
end

context "when using bun.lock" do
let(:project_name) { "bun/simple_v0" }

it do
expect(files_requiring_update).to contain_exactly(
project_dependency_file("package.json"),
project_dependency_file("bun.lock")
)
end
end

context "with multiple dependencies" do
let(:project_name) { "npm6_and_yarn/nested_dependency_update" }
let(:updated_dependencies) { [dependency, other_dependency] }
Expand Down
77 changes: 77 additions & 0 deletions npm_and_yarn/spec/dependabot/npm_and_yarn/file_fetcher_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,20 @@
body: nil,
headers: json_header
)
stub_request(:get, File.join(url, "bun.lock?ref=sha"))
.with(headers: { "Authorization" => "token token" })
.to_return(
status: 404,
body: nil,
headers: json_header
)
stub_request(:get, File.join(url, "packages/bun.lock?ref=sha"))
.with(headers: { "Authorization" => "token token" })
.to_return(
status: 404,
body: nil,
headers: json_header
)
# FileFetcher will iterate trying to find `pnpm-lock.yaml` upwards in the folder tree
stub_request(:get, File.join(url, "packages/pnpm-lock.yaml?ref=sha"))
.with(headers: { "Authorization" => "token token" })
Expand Down Expand Up @@ -488,6 +502,57 @@
end
end

context "with a bun.lock but no package-lock.json file" do
before do
stub_request(:get, url + "?ref=sha")
.with(headers: { "Authorization" => "token token" })
.to_return(
status: 200,
body: fixture("github", "contents_js_bun.json"),
headers: json_header
)
stub_request(:get, File.join(url, "package-lock.json?ref=sha"))
.with(headers: { "Authorization" => "token token" })
.to_return(status: 404)
stub_request(:get, File.join(url, "bun.lock?ref=sha"))
.with(headers: { "Authorization" => "token token" })
.to_return(
status: 200,
body: fixture("github", "bun_lock_content.json"),
headers: json_header
)
end

describe "fetching and parsing the bun.lock" do
before do
allow(Dependabot::Experiments).to receive(:enabled?)
allow(Dependabot::Experiments).to receive(:enabled?).with(:bun_updates).and_return(enable_bun_updates)
end

context "when the experiment :bun_updates is inactive" do
let(:enable_bun_updates) { false }

it "does not fetch or parse the the bun.lock" do
expect(file_fetcher_instance.files.map(&:name))
.to match_array(%w(package.json))
expect(file_fetcher_instance.ecosystem_versions)
.to match({ package_managers: { "unknown" => an_instance_of(Integer) } })
end
end

context "when the experiment :bun_updates is active" do
let(:enable_bun_updates) { true }

it "fetches and parses the bun.lock" do
expect(file_fetcher_instance.files.map(&:name))
.to match_array(%w(package.json bun.lock))
expect(file_fetcher_instance.ecosystem_versions)
.to match({ package_managers: { "bun" => an_instance_of(Integer) } })
end
end
end
end

context "with an npm-shrinkwrap.json but no package-lock.json file" do
before do
stub_request(:get, url + "?ref=sha")
Expand Down Expand Up @@ -1271,6 +1336,12 @@
"pnpm-lock.yaml?ref=sha"
).with(headers: { "Authorization" => "token token" })
.to_return(status: 404)
stub_request(
:get,
"https://api.github.com/repos/gocardless/bump/contents/" \
"bun.lock?ref=sha"
).with(headers: { "Authorization" => "token token" })
.to_return(status: 404)
end

it "fetches package.json from the workspace dependencies" do
Expand Down Expand Up @@ -1840,6 +1911,12 @@
"pnpm-lock.yaml?ref=sha"
).with(headers: { "Authorization" => "token token" })
.to_return(status: 404)
stub_request(
:get,
"https://api.github.com/repos/gocardless/bump/contents/" \
"bun.lock?ref=sha"
).with(headers: { "Authorization" => "token token" })
.to_return(status: 404)
end

it "fetches package.json from the workspace dependencies" do
Expand Down
18 changes: 18 additions & 0 deletions npm_and_yarn/spec/fixtures/github/bun_lock_content.json

Large diffs are not rendered by default.

50 changes: 50 additions & 0 deletions npm_and_yarn/spec/fixtures/github/contents_js_bun.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
[
{
"name": "bun.lock",
"path": "bun.lock",
"sha": "5b0a45fe1229f38af2dedd390b9c531458b07262",
"size": 102275,
"url": "https://api.github.com/repos/toddledev/toddle/contents/bun.lock?ref=main",
"html_url": "https://github.com/toddledev/toddle/blob/main/bun.lock",
"git_url": "https://api.github.com/repos/toddledev/toddle/git/blobs/5b0a45fe1229f38af2dedd390b9c531458b07262",
"download_url": "https://raw.githubusercontent.com/toddledev/toddle/main/bun.lock",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/toddledev/toddle/contents/bun.lock?ref=main",
"git": "https://api.github.com/repos/toddledev/toddle/git/blobs/5b0a45fe1229f38af2dedd390b9c531458b07262",
"html": "https://github.com/toddledev/toddle/blob/main/bun.lock"
}
},
{
"name": "bunfig.toml",
"path": "bunfig.toml",
"sha": "d8bd900b81f2e7462aaa38a3bd592040f5e9ba78",
"size": 33,
"url": "https://api.github.com/repos/toddledev/toddle/contents/bunfig.toml?ref=main",
"html_url": "https://github.com/toddledev/toddle/blob/main/bunfig.toml",
"git_url": "https://api.github.com/repos/toddledev/toddle/git/blobs/d8bd900b81f2e7462aaa38a3bd592040f5e9ba78",
"download_url": "https://raw.githubusercontent.com/toddledev/toddle/main/bunfig.toml",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/toddledev/toddle/contents/bunfig.toml?ref=main",
"git": "https://api.github.com/repos/toddledev/toddle/git/blobs/d8bd900b81f2e7462aaa38a3bd592040f5e9ba78",
"html": "https://github.com/toddledev/toddle/blob/main/bunfig.toml"
}
},
{
"name": "package.json",
"path": "package.json",
"sha": "1393fcaa2e31945e9599741fbf04ed271cb7de22",
"size": 1371,
"url": "https://api.github.com/repos/toddledev/toddle/contents/package.json?ref=main",
"html_url": "https://github.com/toddledev/toddle/blob/main/package.json",
"git_url": "https://api.github.com/repos/toddledev/toddle/git/blobs/1393fcaa2e31945e9599741fbf04ed271cb7de22",
"download_url": "https://raw.githubusercontent.com/toddledev/toddle/main/package.json",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/toddledev/toddle/contents/package.json?ref=main",
"git": "https://api.github.com/repos/toddledev/toddle/git/blobs/1393fcaa2e31945e9599741fbf04ed271cb7de22",
"html": "https://github.com/toddledev/toddle/blob/main/package.json"
}
}
]

0 comments on commit 492491c

Please sign in to comment.