From 0ec20f689ae0074d7e0284865047fbcfadaec589 Mon Sep 17 00:00:00 2001 From: Kamil Bukum Date: Thu, 16 Jan 2025 14:53:45 -0800 Subject: [PATCH 1/4] change no change into dev dependency workspace is not supported dependency error. --- common/lib/dependabot/config/update_config.rb | 3 +- common/lib/dependabot/dependency.rb | 9 ++- common/lib/dependabot/errors.rb | 57 +++++++++++++++++++ .../file_parsers/base/dependency_set.rb | 1 + common/lib/dependabot/git_commit_checker.rb | 1 + common/lib/dependabot/update_checkers/base.rb | 2 + .../dependabot/npm_and_yarn/file_parser.rb | 15 ++++- .../dependabot/npm_and_yarn/file_updater.rb | 28 +++++++++ .../pnpm_lockfile_updater_spec.rb | 2 + 9 files changed, 115 insertions(+), 3 deletions(-) diff --git a/common/lib/dependabot/config/update_config.rb b/common/lib/dependabot/config/update_config.rb index a027bacd75..505fbb1fb3 100644 --- a/common/lib/dependabot/config/update_config.rb +++ b/common/lib/dependabot/config/update_config.rb @@ -53,7 +53,8 @@ def extract_base_version_from_requirement(dependency) name: dependency.name, version: version, requirements: dependency.requirements, - package_manager: dependency.package_manager + package_manager: dependency.package_manager, + workspace: dependency.workspace ) end diff --git a/common/lib/dependabot/dependency.rb b/common/lib/dependabot/dependency.rb index 16658f6877..965ef46b89 100644 --- a/common/lib/dependabot/dependency.rb +++ b/common/lib/dependabot/dependency.rb @@ -88,6 +88,9 @@ def self.register_name_normaliser(package_manager, name_builder) sig { returns(T::Hash[Symbol, T.untyped]) } attr_reader :metadata + sig { returns(T.nilable(String)) } + attr_reader :workspace + # rubocop:disable Metrics/AbcSize # rubocop:disable Metrics/PerceivedComplexity sig do @@ -100,6 +103,7 @@ def self.register_name_normaliser(package_manager, name_builder) previous_version: T.nilable(String), previous_requirements: T.nilable(T::Array[T::Hash[T.any(Symbol, String), T.untyped]]), directory: T.nilable(String), + workspace: T.nilable(String), subdependency_metadata: T.nilable(T::Array[T::Hash[T.any(Symbol, String), String]]), removed: T::Boolean, metadata: T.nilable(T::Hash[T.any(Symbol, String), String]) @@ -107,7 +111,7 @@ def self.register_name_normaliser(package_manager, name_builder) end def initialize(name:, requirements:, package_manager:, version: nil, previous_version: nil, previous_requirements: nil, directory: nil, - subdependency_metadata: [], removed: false, metadata: {}) + workspace: nil, subdependency_metadata: [], removed: false, metadata: {}) @name = name @version = T.let( case version @@ -126,6 +130,8 @@ def initialize(name:, requirements:, package_manager:, version: nil, ) @package_manager = package_manager @directory = directory + @workspace = workspace + unless top_level? || subdependency_metadata == [] @subdependency_metadata = T.let( subdependency_metadata&.map { |h| symbolize_keys(h) }, @@ -166,6 +172,7 @@ def to_h "previous_version" => previous_version, "previous_requirements" => previous_requirements, "directory" => directory, + "workspace" => workspace, "package_manager" => package_manager, "subdependency_metadata" => subdependency_metadata, "removed" => removed? ? true : nil diff --git a/common/lib/dependabot/errors.rb b/common/lib/dependabot/errors.rb index 2171a9ea62..41069a15f1 100644 --- a/common/lib/dependabot/errors.rb +++ b/common/lib/dependabot/errors.rb @@ -33,6 +33,15 @@ def self.fetcher_error_details(error) "supported-versions": error.supported_versions } } + when Dependabot::ToolFeatureNotSupported + { + "error-type": "tool_feature_not_supported", + "error-detail": { + "tool-name": error.tool_name, + "tool-type": error.tool_type, + feature: error.feature + } + } when Dependabot::BranchNotFound { "error-type": "branch_not_found", @@ -103,6 +112,15 @@ def self.fetcher_error_details(error) sig { params(error: StandardError).returns(T.nilable(T::Hash[Symbol, T.untyped])) } def self.parser_error_details(error) case error + when Dependabot::ToolFeatureNotSupported + { + "error-type": "tool_feature_not_supported", + "error-detail": { + "tool-name": error.tool_name, + "tool-type": error.tool_type, + feature: error.feature + } + } when Dependabot::DependencyFileNotEvaluatable { "error-type": "dependency_file_not_evaluatable", @@ -170,6 +188,15 @@ def self.parser_error_details(error) sig { params(error: StandardError).returns(T.nilable(T::Hash[Symbol, T.untyped])) } def self.updater_error_details(error) case error + when Dependabot::ToolFeatureNotSupported + { + "error-type": "tool_feature_not_supported", + "error-detail": { + "tool-name": error.tool_name, + "tool-type": error.tool_type, + feature: error.feature + } + } when Dependabot::DependencyFileNotResolvable { "error-type": "dependency_file_not_resolvable", @@ -300,6 +327,7 @@ def self.updater_error_details(error) } end end + # rubocop:enable Metrics/MethodLength # rubocop:enable Metrics/CyclomaticComplexity # rubocop:enable Lint/RedundantCopDisableDirective @@ -490,6 +518,35 @@ def initialize(tool_name, detected_version, supported_versions) end end + class ToolFeatureNotSupported < DependabotError + extend T::Sig + + sig { returns(String) } + attr_reader :tool_name, :tool_type, :feature + + sig do + params( + tool_name: String, + tool_type: String, + feature: String + ).void + end + def initialize(tool_name:, tool_type:, feature:) + @tool_name = tool_name + @tool_type = tool_type + @feature = feature + super(build_message) + end + + private + + sig { returns(String) } + def build_message + "Dependabot doesn't support the feature '#{feature}' for #{tool_name} (#{tool_type}). " \ + "Please refer to the documentation for supported features." + end + end + class DependencyFileNotFound < DependabotError extend T::Sig diff --git a/common/lib/dependabot/file_parsers/base/dependency_set.rb b/common/lib/dependabot/file_parsers/base/dependency_set.rb index b168b31a6a..8dd032d066 100644 --- a/common/lib/dependabot/file_parsers/base/dependency_set.rb +++ b/common/lib/dependabot/file_parsers/base/dependency_set.rb @@ -156,6 +156,7 @@ def combined_dependency(old_dep, new_dep) version: version, requirements: requirements, package_manager: old_dep.package_manager, + workspace: old_dep.workspace, metadata: old_dep.metadata, subdependency_metadata: subdependency_metadata ) diff --git a/common/lib/dependabot/git_commit_checker.rb b/common/lib/dependabot/git_commit_checker.rb index ff52536978..4ff1699728 100644 --- a/common/lib/dependabot/git_commit_checker.rb +++ b/common/lib/dependabot/git_commit_checker.rb @@ -478,6 +478,7 @@ def listing_source_url candidate_dep = Dependency.new( name: dependency.name, version: dependency.version, + workspace: dependency.workspace, requirements: [], package_manager: dependency.package_manager ) diff --git a/common/lib/dependabot/update_checkers/base.rb b/common/lib/dependabot/update_checkers/base.rb index 05501b2d5c..dedce5b2bd 100644 --- a/common/lib/dependabot/update_checkers/base.rb +++ b/common/lib/dependabot/update_checkers/base.rb @@ -233,6 +233,7 @@ def updated_dependency_without_unlock previous_version: previous_version, previous_requirements: dependency.requirements, package_manager: dependency.package_manager, + workspace: dependency.workspace, metadata: dependency.metadata, subdependency_metadata: dependency.subdependency_metadata ) @@ -250,6 +251,7 @@ def updated_dependency_with_own_req_unlock previous_version: previous_version, previous_requirements: dependency.requirements, package_manager: dependency.package_manager, + workspace: dependency.workspace, metadata: dependency.metadata, subdependency_metadata: dependency.subdependency_metadata ) diff --git a/npm_and_yarn/lib/dependabot/npm_and_yarn/file_parser.rb b/npm_and_yarn/lib/dependabot/npm_and_yarn/file_parser.rb index 7f86e81eaf..0ae99eb8e1 100644 --- a/npm_and_yarn/lib/dependabot/npm_and_yarn/file_parser.rb +++ b/npm_and_yarn/lib/dependabot/npm_and_yarn/file_parser.rb @@ -258,7 +258,19 @@ def build_dependency(file:, type:, name:, requirement:) return if lockfile_details && !version return if ignore_requirement?(requirement) - return if workspace_package_names.include?(name) + + workspace_name = nil + + if Dependabot::Experiments.enabled?(:enable_fix_for_pnpm_no_change_error) + workspaces = workspace_package_names + + return if workspaces.include?(name) + + # Extract workspace name by finding the first match in workspace_package_names + workspace_name = workspaces.find do |workspace| + file.name.start_with?(workspace + "/") + end + end # TODO: Handle aliased packages: # https://github.com/dependabot/dependabot-core/pull/1115 @@ -271,6 +283,7 @@ def build_dependency(file:, type:, name:, requirement:) name: name, version: converted_version, package_manager: ECOSYSTEM, + workspace: workspace_name, requirements: [{ requirement: requirement_for(requirement), file: file.name, diff --git a/npm_and_yarn/lib/dependabot/npm_and_yarn/file_updater.rb b/npm_and_yarn/lib/dependabot/npm_and_yarn/file_updater.rb index 838c48eb56..087aee8424 100644 --- a/npm_and_yarn/lib/dependabot/npm_and_yarn/file_updater.rb +++ b/npm_and_yarn/lib/dependabot/npm_and_yarn/file_updater.rb @@ -48,6 +48,8 @@ def self.updated_files_regex ] end + # rubocop:disable Metrics/AbcSize + # rubocop:disable Metrics/PerceivedComplexity sig { override.returns(T::Array[DependencyFile]) } def updated_dependency_files updated_files = T.let([], T::Array[DependencyFile]) @@ -56,6 +58,30 @@ def updated_dependency_files updated_files += updated_lockfiles if updated_files.none? + + if Dependabot::Experiments.enabled?(:enable_fix_for_pnpm_no_change_error) + all_workspaces_present = dependencies.all? do |dependency| + !dependency.workspace.nil? && !dependency.workspace&.empty? + end + + all_dev_dependencies = dependencies.all? do |dependency| + dependency.requirements.all? do |requirement| + requirement[:groups].include?("devDependencies") + end + end + + if dependencies.any?(&:top_level?) && + pnpm_locks.any? && + all_workspaces_present && + all_dev_dependencies + raise ToolFeatureNotSupported.new( + tool_name: PNPMPackageManager::NAME, + tool_type: "package_manager", + feature: "updating dev dependencies in workspaces" + ) + end + end + raise NoChangeError.new( message: "No files were updated!", error_context: error_context(updated_files: updated_files) @@ -72,6 +98,8 @@ def updated_dependency_files vendor_updated_files(updated_files) end + # rubocop:enable Metrics/AbcSize + # rubocop:enable Metrics/PerceivedComplexity private diff --git a/npm_and_yarn/spec/dependabot/npm_and_yarn/file_updater/pnpm_lockfile_updater_spec.rb b/npm_and_yarn/spec/dependabot/npm_and_yarn/file_updater/pnpm_lockfile_updater_spec.rb index 0cd5064af5..839813af66 100644 --- a/npm_and_yarn/spec/dependabot/npm_and_yarn/file_updater/pnpm_lockfile_updater_spec.rb +++ b/npm_and_yarn/spec/dependabot/npm_and_yarn/file_updater/pnpm_lockfile_updater_spec.rb @@ -72,6 +72,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(:enable_fix_for_pnpm_no_change_error).and_return(true) end after do From 1d0e43cedb8ddebea3a9fd658a058d51011e36da Mon Sep 17 00:00:00 2001 From: Kamil Bukum Date: Thu, 16 Jan 2025 14:54:44 -0800 Subject: [PATCH 2/4] fix lint issue --- npm_and_yarn/lib/dependabot/npm_and_yarn/file_updater.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/npm_and_yarn/lib/dependabot/npm_and_yarn/file_updater.rb b/npm_and_yarn/lib/dependabot/npm_and_yarn/file_updater.rb index 087aee8424..091a3d785b 100644 --- a/npm_and_yarn/lib/dependabot/npm_and_yarn/file_updater.rb +++ b/npm_and_yarn/lib/dependabot/npm_and_yarn/file_updater.rb @@ -50,6 +50,8 @@ def self.updated_files_regex # rubocop:disable Metrics/AbcSize # rubocop:disable Metrics/PerceivedComplexity + # rubocop:disable Metrics/CyclomaticComplexity + # rubocop:disable Metrics/MethodLength sig { override.returns(T::Array[DependencyFile]) } def updated_dependency_files updated_files = T.let([], T::Array[DependencyFile]) @@ -100,6 +102,8 @@ def updated_dependency_files end # rubocop:enable Metrics/AbcSize # rubocop:enable Metrics/PerceivedComplexity + # rubocop:enable Metrics/CyclomaticComplexity + # rubocop:enable Metrics/MethodLength private From 9fdf22c277d9e5a03ee4152924e9dc6b5c3d6050 Mon Sep 17 00:00:00 2001 From: Kamil Bukum Date: Thu, 16 Jan 2025 14:59:28 -0800 Subject: [PATCH 3/4] fix lint issue --- npm_and_yarn/lib/dependabot/npm_and_yarn/file_parser.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/npm_and_yarn/lib/dependabot/npm_and_yarn/file_parser.rb b/npm_and_yarn/lib/dependabot/npm_and_yarn/file_parser.rb index 0ae99eb8e1..f959b95298 100644 --- a/npm_and_yarn/lib/dependabot/npm_and_yarn/file_parser.rb +++ b/npm_and_yarn/lib/dependabot/npm_and_yarn/file_parser.rb @@ -237,6 +237,8 @@ def lockfile_dependencies lockfile_parser.parse_set end + # rubocop:disable Metrics/MethodLength + # rubocop:disable Metrics/PerceivedComplexity sig do params(file: DependencyFile, type: T.untyped, name: String, requirement: String) .returns(T.nilable(Dependency)) @@ -292,6 +294,8 @@ def build_dependency(file:, type:, name:, requirement:) }] ) end + # rubocop:enable Metrics/MethodLength + # rubocop:enable Metrics/PerceivedComplexity sig { override.void } def check_required_files From 6bdc9df61fee692ef66ca4d7ae5739c3b418bae2 Mon Sep 17 00:00:00 2001 From: Kamil Bukum Date: Fri, 17 Jan 2025 08:38:29 -0800 Subject: [PATCH 4/4] add feature flag to specs --- .../npm_and_yarn/file_updater/bun_lockfile_updater_spec.rb | 2 ++ .../npm_and_yarn/file_updater/npm_lockfile_updater_spec.rb | 2 ++ .../npm_and_yarn/file_updater/yarn_lockfile_updater_spec.rb | 2 ++ .../spec/dependabot/npm_and_yarn/update_checker_spec.rb | 2 ++ 4 files changed, 8 insertions(+) diff --git a/npm_and_yarn/spec/dependabot/npm_and_yarn/file_updater/bun_lockfile_updater_spec.rb b/npm_and_yarn/spec/dependabot/npm_and_yarn/file_updater/bun_lockfile_updater_spec.rb index 66604cd493..3f232210e0 100644 --- a/npm_and_yarn/spec/dependabot/npm_and_yarn/file_updater/bun_lockfile_updater_spec.rb +++ b/npm_and_yarn/spec/dependabot/npm_and_yarn/file_updater/bun_lockfile_updater_spec.rb @@ -67,6 +67,8 @@ FileUtils.mkdir_p(tmp_path) allow(Dependabot::Experiments).to receive(:enabled?) .with(:enable_shared_helpers_command_timeout).and_return(true) + allow(Dependabot::Experiments).to receive(:enabled?) + .with(:enable_fix_for_pnpm_no_change_error).and_return(true) end after do diff --git a/npm_and_yarn/spec/dependabot/npm_and_yarn/file_updater/npm_lockfile_updater_spec.rb b/npm_and_yarn/spec/dependabot/npm_and_yarn/file_updater/npm_lockfile_updater_spec.rb index 739cd770e5..9ed2baf7f4 100644 --- a/npm_and_yarn/spec/dependabot/npm_and_yarn/file_updater/npm_lockfile_updater_spec.rb +++ b/npm_and_yarn/spec/dependabot/npm_and_yarn/file_updater/npm_lockfile_updater_spec.rb @@ -76,6 +76,8 @@ .with(:enable_shared_helpers_command_timeout).and_return(true) allow(Dependabot::Experiments).to receive(:enabled?) .with(:npm_v6_deprecation_warning).and_return(true) + allow(Dependabot::Experiments).to receive(:enabled?) + .with(:enable_fix_for_pnpm_no_change_error).and_return(true) end after do diff --git a/npm_and_yarn/spec/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater_spec.rb b/npm_and_yarn/spec/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater_spec.rb index af955adf0b..74acf56038 100644 --- a/npm_and_yarn/spec/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater_spec.rb +++ b/npm_and_yarn/spec/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater_spec.rb @@ -67,6 +67,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(:enable_fix_for_pnpm_no_change_error).and_return(true) end after do diff --git a/npm_and_yarn/spec/dependabot/npm_and_yarn/update_checker_spec.rb b/npm_and_yarn/spec/dependabot/npm_and_yarn/update_checker_spec.rb index cb2f35b6e1..8b0037b2a8 100644 --- a/npm_and_yarn/spec/dependabot/npm_and_yarn/update_checker_spec.rb +++ b/npm_and_yarn/spec/dependabot/npm_and_yarn/update_checker_spec.rb @@ -75,6 +75,8 @@ .with(:enable_shared_helpers_command_timeout).and_return(true) allow(Dependabot::Experiments).to receive(:enabled?) .with(:npm_v6_deprecation_warning).and_return(true) + allow(Dependabot::Experiments).to receive(:enabled?) + .with(:enable_fix_for_pnpm_no_change_error).and_return(true) end after do