Skip to content

Commit

Permalink
Fix: Ensure Compatibility with npm >= 8 to Prevent Lockfile Downgrades (
Browse files Browse the repository at this point in the history
  • Loading branch information
kbukum1 authored Nov 22, 2024
1 parent 2e8d04f commit 64376d8
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 9 deletions.
4 changes: 4 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 @@ -174,6 +174,10 @@ def self.fetch_yarnrc_yml_value(key, default_value)
def self.npm8?(package_lock)
return true unless package_lock&.content

if Dependabot::Experiments.enabled?(:enable_corepack_for_npm_and_yarn)
return npm_version_numeric_latest(package_lock) >= NPM_V8
end

npm_version_numeric(package_lock) == NPM_V8
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ def update_subdependency_in_lockfile(lockfile)
run_yarn_updater(path, lockfile_name)
elsif lockfile.name.end_with?("pnpm-lock.yaml")
run_pnpm_updater(path, lockfile_name)
elsif Helpers.npm8?(lockfile)
run_npm8_updater(path, lockfile_name)
elsif !Helpers.npm8?(lockfile)
run_npm6_updater(path, lockfile_name)
else
run_npm_updater(path, lockfile_name)
end
Expand Down Expand Up @@ -143,7 +143,7 @@ def run_pnpm_updater(path, lockfile_name)
end
end

def run_npm8_updater(path, lockfile_name)
def run_npm_updater(path, lockfile_name)
SharedHelpers.with_git_configured(credentials: credentials) do
Dir.chdir(path) do
NativeHelpers.run_npm8_subdependency_update_command([dependency.name])
Expand All @@ -153,7 +153,7 @@ def run_npm8_updater(path, lockfile_name)
end
end

def run_npm_updater(path, lockfile_name)
def run_npm6_updater(path, lockfile_name)
SharedHelpers.with_git_configured(credentials: credentials) do
Dir.chdir(path) do
SharedHelpers.run_helper_subprocess(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1089,7 +1089,7 @@
let(:previous_requirements) { requirements }

it "raises a helpful error" do
expect { updated_npm_lock_content }.to raise_error(Dependabot::InconsistentRegistryResponse)
expect { updated_npm_lock_content }.to raise_error(Dependabot::DependencyFileNotResolvable)
end
end

Expand Down
89 changes: 89 additions & 0 deletions npm_and_yarn/spec/dependabot/npm_and_yarn/helpers_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -204,4 +204,93 @@
described_class.install("npm", "7.0.0")
end
end

describe "::npm8?" do
let(:lockfile_with_v3) do
Dependabot::DependencyFile.new(name: "package-lock.json", content: { lockfileVersion: 3 }.to_json)
end
let(:lockfile_with_v2) do
Dependabot::DependencyFile.new(name: "package-lock.json", content: { lockfileVersion: 2 }.to_json)
end
let(:empty_lockfile) { Dependabot::DependencyFile.new(name: "package-lock.json", content: "") }
let(:nil_lockfile) { nil }

context "when the feature flag :enable_corepack_for_npm_and_yarn is enabled" do
before do
allow(Dependabot::Experiments).to receive(:enabled?).with(:enable_corepack_for_npm_and_yarn).and_return(true)
end

it "returns true if lockfileVersion is 3 or higher" do
expect(described_class.npm8?(lockfile_with_v3)).to be true
end

it "returns true if lockfileVersion is 2" do
expect(described_class.npm8?(lockfile_with_v2)).to be true
end

it "returns true if lockfile is empty" do
expect(described_class.npm8?(empty_lockfile)).to be true
end

it "returns true if lockfile is nil" do
expect(described_class.npm8?(nil_lockfile)).to be true
end
end

context "when the feature flag :enable_corepack_for_npm_and_yarn is disabled" do
before do
allow(Dependabot::Experiments).to receive(:enabled?).with(:enable_corepack_for_npm_and_yarn).and_return(false)
end

context "when :npm_fallback_version_above_v6 is enabled" do
before do
allow(Dependabot::Experiments).to receive(:enabled?).with(:npm_fallback_version_above_v6).and_return(true)
end

it "returns true if lockfileVersion is 2 or higher" do
expect(described_class.npm8?(lockfile_with_v2)).to be true
end

it "returns true if lockfileVersion is 3 or higher" do
expect(described_class.npm8?(lockfile_with_v3)).to be true
end

it "returns true if lockfile is empty" do
expect(described_class.npm8?(empty_lockfile)).to be true
end

it "returns true if lockfile is nil" do
expect(described_class.npm8?(nil_lockfile)).to be true
end
end

context "when :npm_fallback_version_above_v6 is disabled" do
before do
allow(Dependabot::Experiments).to receive(:enabled?).with(:npm_fallback_version_above_v6).and_return(false)
end

it "returns false for lockfileVersion < 2" do
lockfile_with_v1 = Dependabot::DependencyFile.new(name: "package-lock.json",
content: { lockfileVersion: 1 }.to_json)
expect(described_class.npm8?(lockfile_with_v1)).to be false
end

it "returns true for lockfileVersion 2 or higher" do
expect(described_class.npm8?(lockfile_with_v2)).to be true
end

it "returns true for lockfileVersion 3 or higher" do
expect(described_class.npm8?(lockfile_with_v3)).to be true
end

it "returns true if lockfile is empty" do
expect(described_class.npm8?(empty_lockfile)).to be true
end

it "returns false if lockfile is nil" do
expect(described_class.npm8?(nil_lockfile)).to be true
end
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@
end
let(:latest_allowable_version) { "6.0.2" }

# NOTE: The latest vision is 6.0.2, but we can't reach it as other
# NOTE: The latest version is 6.0.2, but we can't reach it as other
# dependencies constrain us
it { is_expected.to eq(Gem::Version.new("5.7.4")) }
end
Expand All @@ -120,7 +120,7 @@
end
let(:latest_allowable_version) { "6.0.2" }

# NOTE: The latest vision is 6.0.2, but we can't reach it as other
# NOTE: The latest version is 6.0.2, but we can't reach it as other
# dependencies constrain us
it { is_expected.to eq(Gem::Version.new("5.7.4")) }
end
Expand All @@ -138,9 +138,19 @@
end
let(:latest_allowable_version) { "6.0.2" }

it "calls run_npm_updater when npm8? is true" do
allow(Dependabot::NpmAndYarn::Helpers).to receive(:npm8?).and_return(true)
expect(resolver).to receive(:run_npm_updater).and_call_original
expect(latest_resolvable_version).to eq(Gem::Version.new("5.7.4"))
end

# NOTE: The latest vision is 6.0.2, but we can't reach it as other
# dependencies constrain us
it { is_expected.to eq(Gem::Version.new("5.7.4")) }
it "calls run_npm6_updater when npm8? is false" do
allow(Dependabot::NpmAndYarn::Helpers).to receive(:npm8?).and_return(false)
expect(resolver).to receive(:run_npm6_updater).and_call_original
expect(latest_resolvable_version).to eq(Gem::Version.new("5.7.4"))
end
end

context "with a npm6 package-lock.json" do
Expand Down Expand Up @@ -228,7 +238,7 @@
end
end

context "when updating a sub dep across both yarn and npm lockfiles" do
context "when updating a sub-dependency across both yarn and npm lockfiles" do
let(:dependency_files) { project_dependency_files("npm6_and_yarn/nested_sub_dependency_update") }

let(:latest_allowable_version) { "2.0.2" }
Expand Down

0 comments on commit 64376d8

Please sign in to comment.