From a4a0ad9326fd196a9739dd7d0175f200b9d5ea88 Mon Sep 17 00:00:00 2001 From: Jamie Magee Date: Sat, 6 Jan 2024 21:43:26 -0800 Subject: [PATCH] Strong type `Dependabot::Version` --- Gemfile | 2 +- Gemfile.lock | 22 +++---- cargo/lib/dependabot/cargo/requirement.rb | 2 +- common/dependabot-common.gemspec | 2 +- common/lib/dependabot/dependency.rb | 4 +- common/lib/dependabot/version.rb | 60 ++++++++++++++++++- .../update_checker/version_resolver.rb | 4 +- docker/lib/dependabot/docker/version.rb | 2 +- gradle/lib/dependabot/gradle/requirement.rb | 2 +- hex/lib/dependabot/hex/requirement.rb | 2 +- maven/lib/dependabot/maven/requirement.rb | 2 +- .../dependabot/npm_and_yarn/requirement.rb | 2 +- .../lib/dependabot/npm_and_yarn/version.rb | 54 +++++++++++++++-- nuget/lib/dependabot/nuget/requirement.rb | 2 +- pub/lib/dependabot/pub/requirement.rb | 2 +- pub/lib/dependabot/pub/version.rb | 32 +++++++++- .../python/language_version_manager.rb | 2 +- python/lib/dependabot/python/requirement.rb | 2 +- ...{parser@3.2.2.4.rbi => parser@3.3.0.1.rbi} | 0 .../lib/dependabot/terraform/requirement.rb | 2 +- terraform/lib/dependabot/terraform/version.rb | 15 ++++- updater/Gemfile.lock | 4 +- 22 files changed, 179 insertions(+), 42 deletions(-) rename sorbet/rbi/gems/{parser@3.2.2.4.rbi => parser@3.3.0.1.rbi} (100%) diff --git a/Gemfile b/Gemfile index 507767f873..8ea9bbb449 100644 --- a/Gemfile +++ b/Gemfile @@ -22,7 +22,7 @@ gemspec path: "swift" gemspec path: "terraform" # Sorbet -gem "sorbet", "0.5.11156", group: :development +gem "sorbet", "0.5.11178", group: :development gem "tapioca", "0.11.14", require: false, group: :development common_gemspec = File.expand_path("common/dependabot-common.gemspec", __dir__) diff --git a/Gemfile.lock b/Gemfile.lock index f984bcf684..3df9362ff3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -29,7 +29,7 @@ PATH opentelemetry-sdk (~> 1.3) parser (>= 2.5, < 4.0) psych (~> 5.0) - sorbet-runtime (~> 0.5.11026) + sorbet-runtime (~> 0.5.11178) toml-rb (>= 1.1.2, < 3.0) PATH @@ -207,7 +207,7 @@ GEM opentelemetry-api (~> 1.0) parallel (1.24.0) parseconfig (1.0.8) - parser (3.2.2.4) + parser (3.3.0.1) ast (~> 2.4.1) racc prettier_print (1.2.1) @@ -231,14 +231,14 @@ GEM sawyer (0.9.2) addressable (>= 2.3.5) faraday (>= 0.17.3, < 3) - sorbet (0.5.11156) - sorbet-static (= 0.5.11156) - sorbet-runtime (0.5.11156) - sorbet-static (0.5.11156-universal-darwin) - sorbet-static (0.5.11156-x86_64-linux) - sorbet-static-and-runtime (0.5.11156) - sorbet (= 0.5.11156) - sorbet-runtime (= 0.5.11156) + sorbet (0.5.11178) + sorbet-static (= 0.5.11178) + sorbet-runtime (0.5.11178) + sorbet-static (0.5.11178-universal-darwin) + sorbet-static (0.5.11178-x86_64-linux) + sorbet-static-and-runtime (0.5.11178) + sorbet (= 0.5.11178) + sorbet-runtime (= 0.5.11178) spoom (1.2.4) erubi (>= 1.10.0) sorbet-static-and-runtime (>= 0.5.10187) @@ -300,7 +300,7 @@ DEPENDENCIES dependabot-terraform! gpgme (~> 2.0) rake (~> 13) - sorbet (= 0.5.11156) + sorbet (= 0.5.11178) stackprof (~> 0.2.16) tapioca (= 0.11.14) webmock (~> 3.18) diff --git a/cargo/lib/dependabot/cargo/requirement.rb b/cargo/lib/dependabot/cargo/requirement.rb index 1f4d8fb4e8..4b63e716cf 100644 --- a/cargo/lib/dependabot/cargo/requirement.rb +++ b/cargo/lib/dependabot/cargo/requirement.rb @@ -36,7 +36,7 @@ def self.parse(obj) return DefaultRequirement if matches[1] == ">=" && matches[2] == "0" - [matches[1] || "=", Cargo::Version.new(matches[2])] + [matches[1] || "=", Cargo::Version.new(T.must(matches[2]))] end # For consistency with other languages, we define a requirements array. diff --git a/common/dependabot-common.gemspec b/common/dependabot-common.gemspec index 538775e768..a815d249d1 100644 --- a/common/dependabot-common.gemspec +++ b/common/dependabot-common.gemspec @@ -41,7 +41,7 @@ Gem::Specification.new do |spec| spec.add_dependency "opentelemetry-sdk", "~> 1.3" spec.add_dependency "parser", ">= 2.5", "< 4.0" spec.add_dependency "psych", "~> 5.0" - spec.add_dependency "sorbet-runtime", "~> 0.5.11026" + spec.add_dependency "sorbet-runtime", "~> 0.5.11178" spec.add_dependency "toml-rb", ">= 1.1.2", "< 3.0" spec.add_development_dependency "debug", "~> 1.8.0" diff --git a/common/lib/dependabot/dependency.rb b/common/lib/dependabot/dependency.rb index eb3877f96a..655c60c712 100644 --- a/common/lib/dependabot/dependency.rb +++ b/common/lib/dependabot/dependency.rb @@ -143,7 +143,7 @@ def removed? def numeric_version return unless version && version_class.correct?(version) - @numeric_version ||= T.let(version_class.new(version), T.nilable(Dependabot::Version)) + @numeric_version ||= T.let(version_class.new(T.must(version)), T.nilable(Dependabot::Version)) end sig { returns(T::Hash[String, T.untyped]) } @@ -300,7 +300,7 @@ def specific_requirements requirements.select { |r| requirement_class.new(r[:requirement]).specific? } end - sig { returns(T.class_of(Gem::Requirement)) } + sig { returns(T.class_of(Dependabot::Requirement)) } def requirement_class Utils.requirement_class_for_package_manager(package_manager) end diff --git a/common/lib/dependabot/version.rb b/common/lib/dependabot/version.rb index a9b278c7d7..ad252c9716 100644 --- a/common/lib/dependabot/version.rb +++ b/common/lib/dependabot/version.rb @@ -1,21 +1,75 @@ -# typed: true +# typed: strong # frozen_string_literal: true +require "sorbet-runtime" + module Dependabot class Version < Gem::Version + extend T::Sig + extend T::Helpers + + abstract! + + sig do + override + .overridable + .params( + version: T.any( + String, + Integer, + Float, + Gem::Version, + NilClass + ) + ) + .void + end def initialize(version) - @original_version = version + @original_version = T.let(version.to_s, String) - super + T.unsafe(super(version)) + end + + sig do + override + .overridable + .params( + version: T.any( + String, + Integer, + Float, + Gem::Version, + NilClass + ) + ) + .returns(Dependabot::Version) + end + def self.new(version) + T.cast(super, Dependabot::Version) end # Opt-in to Rubygems 4 behavior + sig do + override + .overridable + .params( + version: T.any( + String, + Integer, + Float, + Gem::Version, + NilClass + ) + ) + .returns(T::Boolean) + end def self.correct?(version) return false if version.nil? version.to_s.match?(ANCHORED_VERSION_PATTERN) end + sig { overridable.returns(String) } def to_semver @original_version end diff --git a/composer/lib/dependabot/composer/update_checker/version_resolver.rb b/composer/lib/dependabot/composer/update_checker/version_resolver.rb index 22d5dac490..68a0711279 100644 --- a/composer/lib/dependabot/composer/update_checker/version_resolver.rb +++ b/composer/lib/dependabot/composer/update_checker/version_resolver.rb @@ -205,6 +205,7 @@ def lock_git_dependencies(content) end # rubocop:disable Metrics/PerceivedComplexity + # rubocop:disable Metrics/AbcSize def updated_version_requirement_string lower_bound = if requirements_to_unlock == :none @@ -218,7 +219,7 @@ def updated_version_requirement_string .select { |req_string| req_string.match?(VERSION_REGEX) } .map { |req_string| req_string.match(VERSION_REGEX) } .select { |version| requirement_valid?(">= #{version}") } - .max_by { |version| Composer::Version.new(version) } + .max_by { |version| Composer::Version.new(version.to_s) } ">= #{version_for_requirement || 0}" end @@ -239,6 +240,7 @@ def updated_version_requirement_string lower_bound + ", <= #{latest_allowable_version}" end # rubocop:enable Metrics/PerceivedComplexity + # rubocop:enable Metrics/AbcSize # TODO: Extract error handling and share between the lockfile updater # diff --git a/docker/lib/dependabot/docker/version.rb b/docker/lib/dependabot/docker/version.rb index e62475fd7c..9c44054745 100644 --- a/docker/lib/dependabot/docker/version.rb +++ b/docker/lib/dependabot/docker/version.rb @@ -41,7 +41,7 @@ def self.correct?(version) release_part, = parsed_version[:version].split("_", 2) release_part = Tag.new(release_part.chomp(".").chomp("-").chomp("_")).numeric_version || parsed_version - super(release_part) + super(release_part.to_s) rescue ArgumentError # if we can't instantiate a version, it can't be correct false diff --git a/gradle/lib/dependabot/gradle/requirement.rb b/gradle/lib/dependabot/gradle/requirement.rb index cdca32f136..8ed3055839 100644 --- a/gradle/lib/dependabot/gradle/requirement.rb +++ b/gradle/lib/dependabot/gradle/requirement.rb @@ -27,7 +27,7 @@ def self.parse(obj) return DefaultRequirement if matches[1] == ">=" && matches[2] == "0" - [matches[1] || "=", Gradle::Version.new(matches[2])] + [matches[1] || "=", Gradle::Version.new(T.must(matches[2]))] end sig { override.params(requirement_string: T.nilable(String)).returns(T::Array[Requirement]) } diff --git a/hex/lib/dependabot/hex/requirement.rb b/hex/lib/dependabot/hex/requirement.rb index 37884f499a..4de64f0425 100644 --- a/hex/lib/dependabot/hex/requirement.rb +++ b/hex/lib/dependabot/hex/requirement.rb @@ -54,7 +54,7 @@ def self.parse(obj) return DefaultRequirement if matches[1] == ">=" && matches[2] == "0" - [matches[1] || "=", Hex::Version.new(matches[2])] + [matches[1] || "=", Hex::Version.new(T.must(matches[2]))] end def satisfied_by?(version) diff --git a/maven/lib/dependabot/maven/requirement.rb b/maven/lib/dependabot/maven/requirement.rb index 1e22d0572b..d473643c8f 100644 --- a/maven/lib/dependabot/maven/requirement.rb +++ b/maven/lib/dependabot/maven/requirement.rb @@ -27,7 +27,7 @@ def self.parse(obj) return DefaultRequirement if matches[1] == ">=" && matches[2] == "0" - [matches[1] || "=", Maven::Version.new(matches[2])] + [matches[1] || "=", Maven::Version.new(T.must(matches[2]))] end sig { override.params(requirement_string: T.nilable(String)).returns(T::Array[Requirement]) } diff --git a/npm_and_yarn/lib/dependabot/npm_and_yarn/requirement.rb b/npm_and_yarn/lib/dependabot/npm_and_yarn/requirement.rb index 89f5dc1e09..34231a2ec2 100644 --- a/npm_and_yarn/lib/dependabot/npm_and_yarn/requirement.rb +++ b/npm_and_yarn/lib/dependabot/npm_and_yarn/requirement.rb @@ -34,7 +34,7 @@ def self.parse(obj) return DefaultRequirement if matches[1] == ">=" && matches[2] == "0" - [matches[1] || "=", NpmAndYarn::Version.new(matches[2])] + [matches[1] || "=", NpmAndYarn::Version.new(T.must(matches[2]))] end # Returns an array of requirements. At least one requirement from the diff --git a/npm_and_yarn/lib/dependabot/npm_and_yarn/version.rb b/npm_and_yarn/lib/dependabot/npm_and_yarn/version.rb index 9a1626981f..e163e9fd05 100644 --- a/npm_and_yarn/lib/dependabot/npm_and_yarn/version.rb +++ b/npm_and_yarn/lib/dependabot/npm_and_yarn/version.rb @@ -21,7 +21,20 @@ class Version < Dependabot::Version VERSION_PATTERN = T.let(Gem::Version::VERSION_PATTERN + '(\+[0-9a-zA-Z\-.]+)?', String) ANCHORED_VERSION_PATTERN = /\A\s*(#{VERSION_PATTERN})?\s*\z/ - sig { override.params(version: T.nilable(T.any(String, Gem::Version))).returns(T::Boolean) } + sig do + override + .overridable + .params( + version: T.any( + String, + Integer, + Float, + Gem::Version, + NilClass + ) + ) + .returns(T::Boolean) + end def self.correct?(version) version = version.gsub(/^v/, "") if version.is_a?(String) @@ -42,29 +55,58 @@ def self.semver_for(version) version end - sig { override.params(version: T.any(String, Gem::Version)).void } + sig do + override + .params( + version: T.any( + String, + Integer, + Float, + Gem::Version, + NilClass + ) + ) + .void + end def initialize(version) @version_string = T.let(version.to_s, String) version = version.gsub(/^v/, "") if version.is_a?(String) version, @build_info = version.to_s.split("+") if version.to_s.include?("+") - super + super(T.must(version)) + end + + sig do + override + .params( + version: T.any( + String, + Integer, + Float, + Gem::Version, + NilClass + ) + ) + .returns(Dependabot::NpmAndYarn::Version) + end + def self.new(version) + T.cast(super, Dependabot::NpmAndYarn::Version) end sig { returns(Integer) } def major - @major ||= T.let(segments[0] || 0, T.nilable(Integer)) + @major ||= T.let(segments[0].to_i, T.nilable(Integer)) end sig { returns(Integer) } def minor - @minor ||= T.let(segments[1] || 0, T.nilable(Integer)) + @minor ||= T.let(segments[1].to_i, T.nilable(Integer)) end sig { returns(Integer) } def patch - @patch ||= T.let(segments[2] || 0, T.nilable(Integer)) + @patch ||= T.let(segments[2].to_i, T.nilable(Integer)) end sig { params(other: Dependabot::NpmAndYarn::Version).returns(T::Boolean) } diff --git a/nuget/lib/dependabot/nuget/requirement.rb b/nuget/lib/dependabot/nuget/requirement.rb index 4c12d7bc94..73b37f612f 100644 --- a/nuget/lib/dependabot/nuget/requirement.rb +++ b/nuget/lib/dependabot/nuget/requirement.rb @@ -22,7 +22,7 @@ def self.parse(obj) return DefaultRequirement if matches[1] == ">=" && matches[2] == "0" - [matches[1] || "=", Nuget::Version.new(matches[2])] + [matches[1] || "=", Nuget::Version.new(T.must(matches[2]))] end # For consistency with other languages, we define a requirements array. diff --git a/pub/lib/dependabot/pub/requirement.rb b/pub/lib/dependabot/pub/requirement.rb index 4c35b2d811..ac0a89669d 100644 --- a/pub/lib/dependabot/pub/requirement.rb +++ b/pub/lib/dependabot/pub/requirement.rb @@ -35,7 +35,7 @@ def self.parse(obj) return DefaultRequirement if matches[1] == ">=" && matches[2] == "0" - [matches[1] || "=", Pub::Version.new(matches[2])] + [matches[1] || "=", Pub::Version.new(T.must(matches[2]))] end # For consistency with other languages, we define a requirements array. diff --git a/pub/lib/dependabot/pub/version.rb b/pub/lib/dependabot/pub/version.rb index 660c125ed1..a829c1e210 100644 --- a/pub/lib/dependabot/pub/version.rb +++ b/pub/lib/dependabot/pub/version.rb @@ -25,12 +25,25 @@ class Version < Dependabot::Version sig { returns(String) } attr_reader :build_info - sig { override.params(version: T.any(String, Gem::Version)).void } + sig do + override + .overridable + .params( + version: T.any( + String, + Integer, + Float, + Gem::Version, + NilClass + ) + ) + .void + end def initialize(version) @version_string = T.let(version.to_s, String) version, @build_info = version.to_s.split("+") if version.to_s.include?("+") - super + super(T.must(version)) end sig { override.returns(String) } @@ -43,7 +56,20 @@ def inspect # :nodoc: "#<#{self.class} #{@version_string}>" end - sig { override.params(version: T.any(String, Gem::Version)).returns(T::Boolean) } + sig do + override + .overridable + .params( + version: T.any( + String, + Integer, + Float, + Gem::Version, + NilClass + ) + ) + .returns(T::Boolean) + end def self.correct?(version) return false if version.nil? diff --git a/python/lib/dependabot/python/language_version_manager.rb b/python/lib/dependabot/python/language_version_manager.rb index 6465351cf0..30a63e2462 100644 --- a/python/lib/dependabot/python/language_version_manager.rb +++ b/python/lib/dependabot/python/language_version_manager.rb @@ -29,7 +29,7 @@ def install_required_python end def python_major_minor - @python_major_minor ||= Python::Version.new(python_version).segments[0..1].join(".") + @python_major_minor ||= T.must(Python::Version.new(python_version).segments[0..1]).join(".") end def python_version diff --git a/python/lib/dependabot/python/requirement.rb b/python/lib/dependabot/python/requirement.rb index 3cebe2ea61..69cb742ea6 100644 --- a/python/lib/dependabot/python/requirement.rb +++ b/python/lib/dependabot/python/requirement.rb @@ -43,7 +43,7 @@ def self.parse(obj) return DefaultRequirement if matches[1] == ">=" && matches[2] == "0" - [matches[1] || "=", Python::Version.new(matches[2])] + [matches[1] || "=", Python::Version.new(T.must(matches[2]))] end # Returns an array of requirements. At least one requirement from the diff --git a/sorbet/rbi/gems/parser@3.2.2.4.rbi b/sorbet/rbi/gems/parser@3.3.0.1.rbi similarity index 100% rename from sorbet/rbi/gems/parser@3.2.2.4.rbi rename to sorbet/rbi/gems/parser@3.3.0.1.rbi diff --git a/terraform/lib/dependabot/terraform/requirement.rb b/terraform/lib/dependabot/terraform/requirement.rb index 278386924d..14db5f12ca 100644 --- a/terraform/lib/dependabot/terraform/requirement.rb +++ b/terraform/lib/dependabot/terraform/requirement.rb @@ -28,7 +28,7 @@ def self.parse(obj) return DefaultRequirement if matches[1] == ">=" && matches[2] == "0" - [matches[1] || "=", Terraform::Version.new(matches[2])] + [matches[1] || "=", Terraform::Version.new(T.must(matches[2]))] end # For consistency with other languages, we define a requirements array. diff --git a/terraform/lib/dependabot/terraform/version.rb b/terraform/lib/dependabot/terraform/version.rb index fe68b1ef45..762360dbb3 100644 --- a/terraform/lib/dependabot/terraform/version.rb +++ b/terraform/lib/dependabot/terraform/version.rb @@ -14,7 +14,20 @@ module Terraform class Version < Dependabot::Version extend T::Sig - sig { override.params(version: T.any(String, Gem::Version)).void } + sig do + override + .overridable + .params( + version: T.any( + String, + Integer, + Float, + Gem::Version, + NilClass + ) + ) + .void + end def initialize(version) @version_string = T.let(version.to_s, String) super diff --git a/updater/Gemfile.lock b/updater/Gemfile.lock index 1dfad2dbd9..71d8a48a13 100644 --- a/updater/Gemfile.lock +++ b/updater/Gemfile.lock @@ -29,7 +29,7 @@ PATH opentelemetry-sdk (~> 1.3) parser (>= 2.5, < 4.0) psych (~> 5.0) - sorbet-runtime (~> 0.5.11026) + sorbet-runtime (~> 0.5.11178) toml-rb (>= 1.1.2, < 3.0) PATH @@ -313,7 +313,7 @@ GEM faraday (>= 0.17.3, < 3) sentry-raven (3.1.2) faraday (>= 1.0) - sorbet-runtime (0.5.11142) + sorbet-runtime (0.5.11178) stackprof (0.2.25) stringio (3.0.8) terminal-table (3.0.2)