diff --git a/nuget/lib/dependabot/nuget/update_checker/nupkg_fetcher.rb b/nuget/lib/dependabot/nuget/update_checker/nupkg_fetcher.rb index d73e8e3182f..b9f53f9d1eb 100644 --- a/nuget/lib/dependabot/nuget/update_checker/nupkg_fetcher.rb +++ b/nuget/lib/dependabot/nuget/update_checker/nupkg_fetcher.rb @@ -1,23 +1,43 @@ -# typed: true +# typed: strict # frozen_string_literal: true require "nokogiri" -require "zip" require "stringio" +require "sorbet-runtime" +require "zip" + require "dependabot/nuget/http_response_helpers" module Dependabot module Nuget class NupkgFetcher + extend T::Sig + require_relative "repository_finder" + sig do + params( + dependency_urls: T::Array[T::Hash[Symbol, String]], + package_id: String, + package_version: String + ) + .returns(T.nilable(String)) + end def self.fetch_nupkg_buffer(dependency_urls, package_id, package_version) # check all repositories for the first one that has the nupkg - dependency_urls.reduce(nil) do |nupkg_buffer, repository_details| + dependency_urls.reduce(T.let(nil, T.nilable(String))) do |nupkg_buffer, repository_details| nupkg_buffer || fetch_nupkg_buffer_from_repository(repository_details, package_id, package_version) end end + sig do + params( + repository_details: T::Hash[Symbol, T.untyped], + package_id: T.nilable(String), + package_version: T.nilable(String) + ) + .returns(T.nilable(String)) + end def self.fetch_nupkg_url_from_repository(repository_details, package_id, package_version) return unless package_id && package_version && !package_version.empty? @@ -35,6 +55,14 @@ def self.fetch_nupkg_url_from_repository(repository_details, package_id, package package_url end + sig do + params( + repository_details: T::Hash[Symbol, T.untyped], + package_id: String, + package_version: String + ) + .returns(T.nilable(String)) + end def self.fetch_nupkg_buffer_from_repository(repository_details, package_id, package_version) package_url = fetch_nupkg_url_from_repository(repository_details, package_id, package_version) return unless package_url @@ -43,6 +71,14 @@ def self.fetch_nupkg_buffer_from_repository(repository_details, package_id, pack fetch_stream(package_url, auth_header) end + sig do + params( + repository_details: T::Hash[Symbol, T.untyped], + package_id: String, + package_version: String + ) + .returns(T.nilable(String)) + end def self.get_nuget_v3_package_url(repository_details, package_id, package_version) base_url = repository_details[:base_url] unless base_url @@ -57,15 +93,23 @@ def self.get_nuget_v3_package_url(repository_details, package_id, package_versio # rubocop:disable Metrics/CyclomaticComplexity # rubocop:disable Metrics/PerceivedComplexity + sig do + params( + repository_details: T::Hash[Symbol, T.untyped], + package_id: String, + package_version: String + ) + .returns(T.nilable(String)) + end def self.get_nuget_v3_package_url_from_search(repository_details, package_id, package_version) search_url = repository_details[:search_url] return nil unless search_url # get search result search_result_response = fetch_url(search_url, repository_details) - return nil unless search_result_response.status == 200 + return nil unless search_result_response&.status == 200 - search_response_body = HttpResponseHelpers.remove_wrapping_zero_width_chars(search_result_response.body) + search_response_body = HttpResponseHelpers.remove_wrapping_zero_width_chars(T.must(search_result_response).body) search_results = JSON.parse(search_response_body) # find matching package and version @@ -90,15 +134,23 @@ def self.get_nuget_v3_package_url_from_search(repository_details, package_id, pa # rubocop:enable Metrics/PerceivedComplexity # rubocop:enable Metrics/CyclomaticComplexity + sig do + params( + repository_details: T::Hash[Symbol, T.untyped], + package_id: String, + package_version: String + ) + .returns(T.nilable(String)) + end def self.get_nuget_v2_package_url(repository_details, package_id, package_version) # get package XML base_url = repository_details[:base_url].delete_suffix("/") package_url = "#{base_url}/Packages(Id='#{package_id}',Version='#{package_version}')" response = fetch_url(package_url, repository_details) - return nil unless response.status == 200 + return nil unless response&.status == 200 # find relevant element - doc = Nokogiri::XML(response.body) + doc = Nokogiri::XML(T.must(response).body) doc.remove_namespaces! content_element = doc.xpath("/entry/content") @@ -106,6 +158,14 @@ def self.get_nuget_v2_package_url(repository_details, package_id, package_versio nupkg_url end + sig do + params( + stream_url: String, + auth_header: T::Hash[String, String], + max_redirects: Integer + ) + .returns(T.nilable(String)) + end def self.fetch_stream(stream_url, auth_header, max_redirects = 5) current_url = stream_url current_redirects = 0 @@ -128,17 +188,25 @@ def self.fetch_stream(stream_url, auth_header, max_redirects = 5) current_redirects += 1 return nil if current_redirects > max_redirects - current_url = response.headers["Location"] + current_url = T.must(response.headers["Location"]) else return nil end end end + sig do + params( + url: String, + repository_details: T::Hash[Symbol, T.untyped] + ) + .returns(T.nilable(Excon::Response)) + end def self.fetch_url(url, repository_details) fetch_url_with_auth(url, repository_details.fetch(:auth_header)) end + sig { params(url: String, auth_header: T::Hash[T.any(String, Symbol), T.untyped]).returns(Excon::Response) } def self.fetch_url_with_auth(url, auth_header) cache = CacheManager.cache("nupkg_fetcher_cache") cache[url] ||= Dependabot::RegistryClient.get( diff --git a/nuget/lib/dependabot/nuget/update_checker/nuspec_fetcher.rb b/nuget/lib/dependabot/nuget/update_checker/nuspec_fetcher.rb index 76fdbac5326..6e2c11faa64 100644 --- a/nuget/lib/dependabot/nuget/update_checker/nuspec_fetcher.rb +++ b/nuget/lib/dependabot/nuget/update_checker/nuspec_fetcher.rb @@ -1,23 +1,42 @@ -# typed: true +# typed: strict # frozen_string_literal: true require "nokogiri" -require "zip" require "stringio" +require "sorbet-runtime" +require "zip" module Dependabot module Nuget class NuspecFetcher + extend T::Sig + require_relative "nupkg_fetcher" require_relative "repository_finder" + sig do + params( + dependency_urls: T::Array[T::Hash[Symbol, String]], + package_id: String, + package_version: String + ) + .returns(T.nilable(Nokogiri::XML::Document)) + end def self.fetch_nuspec(dependency_urls, package_id, package_version) # check all repositories for the first one that has the nuspec - dependency_urls.reduce(nil) do |nuspec_xml, repository_details| + dependency_urls.reduce(T.let(nil, T.nilable(Nokogiri::XML::Document))) do |nuspec_xml, repository_details| nuspec_xml || fetch_nuspec_from_repository(repository_details, package_id, package_version) end end + sig do + params( + repository_details: T::Hash[Symbol, T.untyped], + package_id: T.nilable(String), + package_version: T.nilable(String) + ) + .returns(T.nilable(Nokogiri::XML::Document)) + end def self.fetch_nuspec_from_repository(repository_details, package_id, package_version) return unless package_id && package_version && !package_version.empty? @@ -55,6 +74,7 @@ def self.fetch_nuspec_from_repository(repository_details, package_id, package_ve nuspec_xml end + sig { params(feed_url: String).returns(T::Boolean) } def self.feed_supports_nuspec_download?(feed_url) feed_regexs = [ # nuget @@ -67,6 +87,7 @@ def self.feed_supports_nuspec_download?(feed_url) feed_regexs.any? { |reg| reg.match(feed_url) } end + sig { params(zip_stream: String, package_id: String).returns(T.nilable(String)) } def self.extract_nuspec(zip_stream, package_id) Zip::File.open_buffer(zip_stream) do |zip| nuspec_entry = zip.find { |entry| entry.name == "#{package_id}.nuspec" } @@ -75,6 +96,7 @@ def self.extract_nuspec(zip_stream, package_id) nil end + sig { params(string: String).returns(String) } def self.remove_invalid_characters(string) string.dup .force_encoding(Encoding::UTF_8) diff --git a/nuget/lib/dependabot/nuget/update_checker/requirements_updater.rb b/nuget/lib/dependabot/nuget/update_checker/requirements_updater.rb index 6e1fca47df0..fe600921c1c 100644 --- a/nuget/lib/dependabot/nuget/update_checker/requirements_updater.rb +++ b/nuget/lib/dependabot/nuget/update_checker/requirements_updater.rb @@ -1,4 +1,4 @@ -# typed: true +# typed: strict # frozen_string_literal: true ####################################################################### @@ -6,6 +6,8 @@ # https://docs.microsoft.com/en-us/nuget/reference/package-versioning # ####################################################################### +require "sorbet-runtime" + require "dependabot/update_checkers/base" require "dependabot/nuget/version" @@ -13,14 +15,25 @@ module Dependabot module Nuget class UpdateChecker < Dependabot::UpdateCheckers::Base class RequirementsUpdater + extend T::Sig + + sig do + params( + requirements: T::Array[T::Hash[Symbol, T.untyped]], + latest_version: T.nilable(T.any(String, Dependabot::Nuget::Version)), + source_details: T.nilable(T::Hash[Symbol, T.untyped]) + ) + .void + end def initialize(requirements:, latest_version:, source_details:) @requirements = requirements @source_details = source_details return unless latest_version - @latest_version = version_class.new(latest_version) + @latest_version = T.let(version_class.new(latest_version), Dependabot::Nuget::Version) end + sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) } def updated_requirements return requirements unless latest_version @@ -52,32 +65,42 @@ def updated_requirements private - attr_reader :requirements, :latest_version, :source_details + sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) } + attr_reader :requirements + + sig { returns(T.nilable(Dependabot::Nuget::Version)) } + attr_reader :latest_version + + sig { returns(T.nilable(T::Hash[Symbol, T.untyped])) } + attr_reader :source_details + sig { returns(T.class_of(Dependabot::Nuget::Version)) } def version_class - Nuget::Version + Dependabot::Nuget::Version end + sig { params(req_string: String).returns(String) } def update_wildcard_requirement(req_string) return req_string if req_string == "*-*" return req_string if req_string == "*" - precision = req_string.split("*").first.split(/\.|\-/).count + precision = T.must(req_string.split("*").first).split(/\.|\-/).count wildcard_section = req_string.partition(/(?=[.\-]\*)/).last - version_parts = latest_version.segments.first(precision) + version_parts = T.must(latest_version).segments.first(precision) version = version_parts.join(".") version + wildcard_section end + sig { returns(T::Hash[Symbol, T.untyped]) } def updated_source { type: "nuget_repo", - url: source_details.fetch(:repo_url), - nuspec_url: source_details.fetch(:nuspec_url), - source_url: source_details.fetch(:source_url) + url: source_details&.fetch(:repo_url), + nuspec_url: source_details&.fetch(:nuspec_url), + source_url: source_details&.fetch(:source_url) } end end