Skip to content

Commit

Permalink
Update npm_and_yarn deprecation and unsupported checks for npm, `…
Browse files Browse the repository at this point in the history
…pnpm`, and `yarn` package managers (#11240)
  • Loading branch information
kbukum1 authored Jan 7, 2025
1 parent 4092299 commit 66735fb
Show file tree
Hide file tree
Showing 14 changed files with 342 additions and 75 deletions.
6 changes: 2 additions & 4 deletions npm_and_yarn/lib/dependabot/npm_and_yarn/helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,7 @@ module Helpers # rubocop:disable Metrics/ModuleLength
# Otherwise, we are going to use old versionining npm 6
sig { params(lockfile: T.nilable(DependencyFile)).returns(Integer) }
def self.npm_version_numeric(lockfile)
if Dependabot::Experiments.enabled?(:enable_corepack_for_npm_and_yarn)
return npm_version_numeric_latest(lockfile)
end
return npm_version_numeric_latest(lockfile) if Dependabot::Experiments.enabled?(:npm_v6_deprecation_warning)

fallback_version_npm8 = Dependabot::Experiments.enabled?(:npm_fallback_version_above_v6)

Expand Down Expand Up @@ -174,7 +172,7 @@ 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)
if Dependabot::Experiments.enabled?(:npm_v6_deprecation_warning)
return npm_version_numeric_latest(package_lock) >= NPM_V8
end

Expand Down
76 changes: 55 additions & 21 deletions npm_and_yarn/lib/dependabot/npm_and_yarn/package_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,16 @@ class NpmPackageManager < Ecosystem::VersionManager

sig do
params(
raw_version: String,
detected_version: T.nilable(String),
raw_version: T.nilable(String),
requirement: T.nilable(Dependabot::NpmAndYarn::Requirement)
).void
end
def initialize(raw_version, requirement: nil)
def initialize(detected_version: nil, raw_version: nil, requirement: nil)
super(
name: NAME,
version: Version.new(raw_version),
detected_version: detected_version ? Version.new(detected_version) : nil,
version: raw_version ? Version.new(raw_version) : nil,
deprecated_versions: DEPRECATED_VERSIONS,
supported_versions: SUPPORTED_VERSIONS,
requirement: requirement
Expand All @@ -88,17 +90,22 @@ def initialize(raw_version, requirement: nil)

sig { override.returns(T::Boolean) }
def deprecated?
return false unless detected_version

return false if unsupported?

return false unless Dependabot::Experiments.enabled?(:npm_v6_deprecation_warning)

deprecated_versions.include?(version)
deprecated_versions.include?(detected_version)
end

sig { override.returns(T::Boolean) }
def unsupported?
return false unless detected_version

return false unless Dependabot::Experiments.enabled?(:npm_v6_unsupported_error)

supported_versions.all? { |supported| supported > version }
supported_versions.all? { |supported| supported > detected_version }
end
end

Expand All @@ -123,14 +130,16 @@ class YarnPackageManager < Ecosystem::VersionManager

sig do
params(
raw_version: String,
requirement: T.nilable(Requirement)
detected_version: T.nilable(String),
raw_version: T.nilable(String),
requirement: T.nilable(Dependabot::NpmAndYarn::Requirement)
).void
end
def initialize(raw_version, requirement: nil)
def initialize(detected_version: nil, raw_version: nil, requirement: nil)
super(
name: NAME,
version: Version.new(raw_version),
detected_version: detected_version ? Version.new(detected_version) : nil,
version: raw_version ? Version.new(raw_version) : nil,
deprecated_versions: DEPRECATED_VERSIONS,
supported_versions: SUPPORTED_VERSIONS,
requirement: requirement
Expand Down Expand Up @@ -168,14 +177,16 @@ class PNPMPackageManager < Ecosystem::VersionManager

sig do
params(
raw_version: String,
requirement: T.nilable(Requirement)
detected_version: T.nilable(String),
raw_version: T.nilable(String),
requirement: T.nilable(Dependabot::NpmAndYarn::Requirement)
).void
end
def initialize(raw_version, requirement: nil)
def initialize(detected_version: nil, raw_version: nil, requirement: nil)
super(
name: NAME,
version: Version.new(raw_version),
detected_version: detected_version ? Version.new(detected_version) : nil,
version: raw_version ? Version.new(raw_version) : nil,
deprecated_versions: DEPRECATED_VERSIONS,
supported_versions: SUPPORTED_VERSIONS,
requirement: requirement
Expand Down Expand Up @@ -284,14 +295,16 @@ class Language < Ecosystem::VersionManager

sig do
params(
detected_version: T.nilable(String),
raw_version: T.nilable(String),
requirement: T.nilable(Requirement)
requirement: T.nilable(Dependabot::NpmAndYarn::Requirement)
).void
end
def initialize(raw_version, requirement: nil)
def initialize(detected_version: nil, raw_version: nil, requirement: nil)
super(
name: NAME,
version: Version.new(raw_version),
detected_version: detected_version ? Version.new(detected_version) : nil,
version: raw_version ? Version.new(raw_version) : nil,
deprecated_versions: DEPRECATED_VERSIONS,
supported_versions: SUPPORTED_VERSIONS,
requirement: requirement
Expand Down Expand Up @@ -349,7 +362,7 @@ def package_manager
sig { returns(Ecosystem::VersionManager) }
def language
@language ||= Language.new(
Helpers.node_version,
raw_version: Helpers.node_version,
requirement: language_requirement
)
end
Expand Down Expand Up @@ -458,17 +471,37 @@ def setup(name)
# rubocop:enable Metrics/PerceivedComplexity
# rubocop:enable Metrics/MethodLength

sig { params(name: String).returns(T.nilable(String)) }
def detect_version(name)
# we prioritize version mentioned in "packageManager" instead of "engines"
if @manifest_package_manager&.start_with?("#{name}@")
detected_version = @manifest_package_manager.split("@").last.to_s
end

# if "packageManager" have no version specified, we check if we can extract "engines" information
detected_version = check_engine_version(name) if !detected_version || detected_version.empty?

# if "packageManager" and "engines" both are not present, we check if we can infer the version
# from the manifest file lockfileVersion
detected_version = guessed_version(name) if !detected_version || detected_version.empty?

detected_version&.to_s
end

sig { params(name: T.nilable(String)).returns(Ecosystem::VersionManager) }
def package_manager_by_name(name)
Dependabot.logger.info("Resolving package manager for: #{name || 'default'}")

name = ensure_valid_package_manager(name)
package_manager_class = T.must(PACKAGE_MANAGER_CLASSES[name])

if name == NpmPackageManager::NAME
detected_version = Helpers.npm_version_numeric_latest(@lockfiles[:npm])
package_manager = package_manager_class.new(detected_version.to_s)
detected_version = detect_version(name)

# if we have a detected version, we check if it is deprecated or unsupported
if detected_version
package_manager = package_manager_class.new(
detected_version: detected_version.to_s
)
return package_manager if package_manager.deprecated? || package_manager.unsupported?
end

Expand All @@ -483,7 +516,8 @@ def package_manager_by_name(name)
end

package_manager_class.new(
installed_version.to_s,
detected_version: detected_version.to_s,
raw_version: installed_version,
requirement: package_manager_requirement
)
rescue StandardError => e
Expand Down
2 changes: 2 additions & 0 deletions npm_and_yarn/spec/dependabot/npm_and_yarn/file_parser_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
.with(:enable_corepack_for_npm_and_yarn).and_return(enable_corepack_for_npm_and_yarn)
allow(Dependabot::Experiments).to receive(:enabled?)
.with(:enable_shared_helpers_command_timeout).and_return(true)
allow(Dependabot::Experiments).to receive(:enabled?)
.with(:npm_v6_deprecation_warning).and_return(true)
end

after do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@
.with(:enable_corepack_for_npm_and_yarn).and_return(enable_corepack_for_npm_and_yarn)
allow(Dependabot::Experiments).to receive(:enabled?)
.with(:enable_shared_helpers_command_timeout).and_return(true)
allow(Dependabot::Experiments).to receive(:enabled?)
.with(:npm_v6_deprecation_warning).and_return(true)
end

after do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@
.with(:enable_corepack_for_npm_and_yarn).and_return(enable_corepack_for_npm_and_yarn)
allow(Dependabot::Experiments).to receive(:enabled?)
.with(:enable_shared_helpers_command_timeout).and_return(true)
allow(Dependabot::Experiments).to receive(:enabled?)
.with(:npm_v6_deprecation_warning).and_return(true)
end

after do
Expand Down
49 changes: 28 additions & 21 deletions npm_and_yarn/spec/dependabot/npm_and_yarn/helpers_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -92,29 +92,29 @@
)
end

it "returns flattened list of dependencies populated with :all_versions metadata" do
dependency_set = Dependabot::NpmAndYarn::FileParser::DependencySet.new
dependency_set << foo_a << bar_a << foo_c << bar_c << foo_b << bar_b
context "when dependencies in set already have :all_versions metadata" do
it "returns flattened list of dependencies populated with :all_versions metadata" do
dependency_set = Dependabot::NpmAndYarn::FileParser::DependencySet.new
dependency_set << foo_a << bar_a << foo_c << bar_c << foo_b << bar_b

expect(described_class.dependencies_with_all_versions_metadata(dependency_set)).to eq([
Dependabot::Dependency.new(
name: "foo",
version: "0.0.1",
requirements: (foo_a.requirements + foo_c.requirements + foo_b.requirements).uniq,
package_manager: "npm_and_yarn",
metadata: { all_versions: [foo_a, foo_c, foo_b] }
),
Dependabot::Dependency.new(
name: "bar",
version: "0.2.1",
requirements: (bar_a.requirements + bar_c.requirements + bar_b.requirements).uniq,
package_manager: "npm_and_yarn",
metadata: { all_versions: [bar_a, bar_c, bar_b] }
)
])
end
expect(described_class.dependencies_with_all_versions_metadata(dependency_set)).to eq([
Dependabot::Dependency.new(
name: "foo",
version: "0.0.1",
requirements: (foo_a.requirements + foo_c.requirements + foo_b.requirements).uniq,
package_manager: "npm_and_yarn",
metadata: { all_versions: [foo_a, foo_c, foo_b] }
),
Dependabot::Dependency.new(
name: "bar",
version: "0.2.1",
requirements: (bar_a.requirements + bar_c.requirements + bar_b.requirements).uniq,
package_manager: "npm_and_yarn",
metadata: { all_versions: [bar_a, bar_c, bar_b] }
)
])
end

context "when dependencies in set already have :all_versions metadata" do
it "correctly merges existing metadata into new metadata" do
dependency_set = Dependabot::NpmAndYarn::FileParser::DependencySet.new
dependency_set << foo_a
Expand Down Expand Up @@ -338,6 +338,7 @@
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)
allow(Dependabot::Experiments).to receive(:enabled?).with(:npm_v6_deprecation_warning).and_return(true)
end

it "returns true if lockfileVersion is 3 or higher" do
Expand All @@ -360,11 +361,17 @@
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)
allow(Dependabot::Experiments).to receive(:enabled?)
.with(:npm_v6_deprecation_warning)
.and_return(true)
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)
allow(Dependabot::Experiments).to receive(:enabled?)
.with(:npm_v6_deprecation_warning)
.and_return(true)
end

it "returns true if lockfileVersion is 2 or higher" do
Expand Down
9 changes: 7 additions & 2 deletions npm_and_yarn/spec/dependabot/npm_and_yarn/language_spec.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
# typed: false
# frozen_string_literal: true

require "dependabot/ecosystem"
require "dependabot/npm_and_yarn/package_manager"
require "dependabot/ecosystem"
require "spec_helper"

RSpec.describe Dependabot::NpmAndYarn::Language do
let(:language) { described_class.new(raw_version, requirement: requirement) }
let(:language) do
described_class.new(
raw_version: raw_version,
requirement: requirement
)
end
let(:raw_version) { "16.13.1" }
let(:requirement) { nil }

Expand Down
Loading

0 comments on commit 66735fb

Please sign in to comment.