dependabot-cargo 0.332.0 → 0.333.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/dependabot/cargo/file_fetcher.rb +13 -2
- data/lib/dependabot/cargo/file_updater/lockfile_updater.rb +2 -1
- data/lib/dependabot/cargo/file_updater/manifest_updater.rb +71 -22
- data/lib/dependabot/cargo/metadata_finder.rb +24 -3
- data/lib/dependabot/cargo/requirement.rb +1 -1
- data/lib/dependabot/cargo/update_checker/file_preparer.rb +52 -5
- data/lib/dependabot/cargo/update_checker/requirements_updater.rb +41 -16
- data/lib/dependabot/cargo/update_checker/version_resolver.rb +2 -1
- data/lib/dependabot/cargo/update_checker.rb +96 -41
- data/lib/dependabot/cargo/version.rb +9 -2
- metadata +6 -6
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: '091190197e180945f1979a4102768e43519a054c4dcccfad73bebd41d6f08abe'
         | 
| 4 | 
            +
              data.tar.gz: 3bc3403ebacc832ea65c9e7cce7fcf8bd39728548bf8bf9c516bf6d07594d9f0
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 3aef8d8668764a7849ea0b03ce683103151b1cbed29d8c0b405767d45805295358328e9a1105b990a8b70413f74bd1e2b045b884311ea7a0a8e6ac6f937e8a03
         | 
| 7 | 
            +
              data.tar.gz: c62c275d2fee1f4477db333d62ed8bcf1a5f2eea1045356e4150a0afccdcda5a65a819b6cb5630d0943e6ebb0a49a9fc7ff82dba3fc1783dc432970de8d27294
         | 
| @@ -7,6 +7,7 @@ require "toml-rb" | |
| 7 7 |  | 
| 8 8 | 
             
            require "dependabot/file_fetchers"
         | 
| 9 9 | 
             
            require "dependabot/file_fetchers/base"
         | 
| 10 | 
            +
            require "dependabot/file_filtering"
         | 
| 10 11 | 
             
            require "dependabot/cargo/file_parser"
         | 
| 11 12 |  | 
| 12 13 | 
             
            # Docs on Cargo workspaces:
         | 
| @@ -30,7 +31,7 @@ module Dependabot | |
| 30 31 | 
             
                  sig { override.returns(T.nilable(T::Hash[Symbol, T.untyped])) }
         | 
| 31 32 | 
             
                  def ecosystem_versions
         | 
| 32 33 | 
             
                    channel = if rust_toolchain
         | 
| 33 | 
            -
                                TomlRB.parse(T.must(rust_toolchain).content). | 
| 34 | 
            +
                                TomlRB.parse(T.must(rust_toolchain).content).dig("toolchain", "channel")
         | 
| 34 35 | 
             
                              else
         | 
| 35 36 | 
             
                                "default"
         | 
| 36 37 | 
             
                              end
         | 
| @@ -55,7 +56,13 @@ module Dependabot | |
| 55 56 | 
             
                    fetched_files << T.must(cargo_config) if cargo_config
         | 
| 56 57 | 
             
                    fetched_files << T.must(rust_toolchain) if rust_toolchain
         | 
| 57 58 | 
             
                    fetched_files += fetch_path_dependency_and_workspace_files
         | 
| 58 | 
            -
             | 
| 59 | 
            +
             | 
| 60 | 
            +
                    # Filter excluded files from final collection
         | 
| 61 | 
            +
                    filtered_files = fetched_files.reject do |file|
         | 
| 62 | 
            +
                      Dependabot::FileFiltering.should_exclude_path?(file.name, "file from final collection", @exclude_paths)
         | 
| 63 | 
            +
                    end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                    filtered_files.uniq
         | 
| 59 66 | 
             
                  end
         | 
| 60 67 |  | 
| 61 68 | 
             
                  private
         | 
| @@ -125,6 +132,8 @@ module Dependabot | |
| 125 132 | 
             
                      next if previously_fetched_files.map(&:name).include?(path)
         | 
| 126 133 | 
             
                      next if file.name == path
         | 
| 127 134 |  | 
| 135 | 
            +
                      next if Dependabot::FileFiltering.should_exclude_path?(path, "file from final collection", @exclude_paths)
         | 
| 136 | 
            +
             | 
| 128 137 | 
             
                      fetched_file = fetch_file_from_host(path, fetch_submodules: true)
         | 
| 129 138 | 
             
                      previously_fetched_files << fetched_file
         | 
| 130 139 | 
             
                      grandchild_requirement_files =
         | 
| @@ -160,6 +169,8 @@ module Dependabot | |
| 160 169 | 
             
                        next if previously_fetched_files.map(&:name).include?(path)
         | 
| 161 170 | 
             
                        next if file.name == path
         | 
| 162 171 |  | 
| 172 | 
            +
                        next if Dependabot::FileFiltering.should_exclude_path?(path, "file from final collection", @exclude_paths)
         | 
| 173 | 
            +
             | 
| 163 174 | 
             
                        fetched_file = fetch_file_from_host(path, fetch_submodules: true)
         | 
| 164 175 | 
             
                                       .tap { |f| f.support_file = true }
         | 
| 165 176 | 
             
                        previously_fetched_files << fetched_file
         | 
| @@ -16,6 +16,7 @@ module Dependabot | |
| 16 16 | 
             
                  # rubocop:disable Metrics/ClassLength
         | 
| 17 17 | 
             
                  class LockfileUpdater
         | 
| 18 18 | 
             
                    extend T::Sig
         | 
| 19 | 
            +
             | 
| 19 20 | 
             
                    LOCKFILE_ENTRY_REGEX = /
         | 
| 20 21 | 
             
                      \[\[package\]\]\n
         | 
| 21 22 | 
             
                      (?:(?!^\[(?:\[package|metadata)).)+
         | 
| @@ -123,7 +124,7 @@ module Dependabot | |
| 123 124 | 
             
                              dependency.version
         | 
| 124 125 | 
             
                            end
         | 
| 125 126 |  | 
| 126 | 
            -
                      if ver && spec_options. | 
| 127 | 
            +
                      if ver && spec_options.one? { |s| s.end_with?(ver) }
         | 
| 127 128 | 
             
                        @custom_specification = spec_options.find { |s| s.end_with?(ver) }
         | 
| 128 129 | 
             
                        return true
         | 
| 129 130 | 
             
                      elsif ver && spec_options.count { |s| s.end_with?(ver) } > 1
         | 
| @@ -1,22 +1,38 @@ | |
| 1 | 
            -
            # typed:  | 
| 1 | 
            +
            # typed: strict
         | 
| 2 2 | 
             
            # frozen_string_literal: true
         | 
| 3 3 |  | 
| 4 | 
            +
            require "sorbet-runtime"
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            require "dependabot/dependency"
         | 
| 7 | 
            +
            require "dependabot/dependency_file"
         | 
| 4 8 | 
             
            require "dependabot/cargo/file_updater"
         | 
| 5 9 |  | 
| 6 10 | 
             
            module Dependabot
         | 
| 7 11 | 
             
              module Cargo
         | 
| 8 12 | 
             
                class FileUpdater
         | 
| 9 13 | 
             
                  class ManifestUpdater
         | 
| 14 | 
            +
                    extend T::Sig
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    sig do
         | 
| 17 | 
            +
                      params(
         | 
| 18 | 
            +
                        dependencies: T::Array[Dependabot::Dependency],
         | 
| 19 | 
            +
                        manifest: Dependabot::DependencyFile
         | 
| 20 | 
            +
                      ).void
         | 
| 21 | 
            +
                    end
         | 
| 10 22 | 
             
                    def initialize(dependencies:, manifest:)
         | 
| 11 | 
            -
                      @dependencies = dependencies
         | 
| 12 | 
            -
                      @manifest = manifest
         | 
| 23 | 
            +
                      @dependencies = T.let(dependencies, T::Array[Dependabot::Dependency])
         | 
| 24 | 
            +
                      @manifest = T.let(manifest, Dependabot::DependencyFile)
         | 
| 13 25 | 
             
                    end
         | 
| 14 26 |  | 
| 27 | 
            +
                    sig { returns(String) }
         | 
| 15 28 | 
             
                    def updated_manifest_content
         | 
| 29 | 
            +
                      content = manifest.content
         | 
| 30 | 
            +
                      raise "Manifest has no content" if content.nil?
         | 
| 31 | 
            +
             | 
| 16 32 | 
             
                      dependencies
         | 
| 17 33 | 
             
                        .select { |dep| requirement_changed?(manifest, dep) }
         | 
| 18 | 
            -
                        .reduce( | 
| 19 | 
            -
                          updated_content =  | 
| 34 | 
            +
                        .reduce(content.dup) do |current_content, dep|
         | 
| 35 | 
            +
                          updated_content = current_content
         | 
| 20 36 |  | 
| 21 37 | 
             
                          updated_content = update_requirements(
         | 
| 22 38 | 
             
                            content: updated_content,
         | 
| @@ -30,7 +46,7 @@ module Dependabot | |
| 30 46 | 
             
                            dependency: dep
         | 
| 31 47 | 
             
                          )
         | 
| 32 48 |  | 
| 33 | 
            -
                          raise "Expected content to change!" if  | 
| 49 | 
            +
                          raise "Expected content to change!" if current_content == updated_content
         | 
| 34 50 |  | 
| 35 51 | 
             
                          updated_content
         | 
| 36 52 | 
             
                        end
         | 
| @@ -38,42 +54,51 @@ module Dependabot | |
| 38 54 |  | 
| 39 55 | 
             
                    private
         | 
| 40 56 |  | 
| 57 | 
            +
                    sig { returns(T::Array[Dependabot::Dependency]) }
         | 
| 41 58 | 
             
                    attr_reader :dependencies
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                    sig { returns(Dependabot::DependencyFile) }
         | 
| 42 61 | 
             
                    attr_reader :manifest
         | 
| 43 62 |  | 
| 63 | 
            +
                    sig { params(file: Dependabot::DependencyFile, dependency: Dependabot::Dependency).returns(T::Boolean) }
         | 
| 44 64 | 
             
                    def requirement_changed?(file, dependency)
         | 
| 45 65 | 
             
                      changed_requirements =
         | 
| 46 | 
            -
                        dependency.requirements - dependency.previous_requirements
         | 
| 66 | 
            +
                        dependency.requirements - (dependency.previous_requirements || [])
         | 
| 47 67 |  | 
| 48 68 | 
             
                      changed_requirements.any? { |f| f[:file] == file.name }
         | 
| 49 69 | 
             
                    end
         | 
| 50 70 |  | 
| 71 | 
            +
                    sig { params(content: String, filename: String, dependency: Dependabot::Dependency).returns(String) }
         | 
| 51 72 | 
             
                    def update_requirements(content:, filename:, dependency:)
         | 
| 52 | 
            -
                      updated_content = content.dup
         | 
| 73 | 
            +
                      updated_content = T.let(content.dup, String)
         | 
| 53 74 |  | 
| 54 75 | 
             
                      # The UpdateChecker ensures the order of requirements is preserved
         | 
| 55 76 | 
             
                      # when updating, so we can zip them together in new/old pairs.
         | 
| 56 77 | 
             
                      reqs = dependency.requirements
         | 
| 57 | 
            -
                                       .zip(dependency.previous_requirements)
         | 
| 78 | 
            +
                                       .zip(dependency.previous_requirements || [])
         | 
| 58 79 | 
             
                                       .reject { |new_req, old_req| new_req == old_req }
         | 
| 59 80 |  | 
| 60 81 | 
             
                      # Loop through each changed requirement
         | 
| 61 82 | 
             
                      reqs.each do |new_req, old_req|
         | 
| 83 | 
            +
                        next if old_req.nil?
         | 
| 84 | 
            +
             | 
| 62 85 | 
             
                        raise "Bad req match" unless new_req[:file] == old_req[:file]
         | 
| 63 86 | 
             
                        next if new_req[:requirement] == old_req[:requirement]
         | 
| 64 87 | 
             
                        next unless new_req[:file] == filename
         | 
| 65 88 |  | 
| 66 | 
            -
                        updated_content = | 
| 67 | 
            -
                           | 
| 68 | 
            -
             | 
| 69 | 
            -
             | 
| 70 | 
            -
             | 
| 71 | 
            -
             | 
| 89 | 
            +
                        updated_content =
         | 
| 90 | 
            +
                          update_manifest_req(
         | 
| 91 | 
            +
                            content: updated_content,
         | 
| 92 | 
            +
                            dep: dependency,
         | 
| 93 | 
            +
                            old_req: old_req.fetch(:requirement),
         | 
| 94 | 
            +
                            new_req: new_req.fetch(:requirement)
         | 
| 95 | 
            +
                          )
         | 
| 72 96 | 
             
                      end
         | 
| 73 97 |  | 
| 74 98 | 
             
                      updated_content
         | 
| 75 99 | 
             
                    end
         | 
| 76 100 |  | 
| 101 | 
            +
                    sig { params(content: String, filename: String, dependency: Dependabot::Dependency).returns(String) }
         | 
| 77 102 | 
             
                    def update_git_pin(content:, filename:, dependency:)
         | 
| 78 103 | 
             
                      updated_pin =
         | 
| 79 104 | 
             
                        dependency.requirements
         | 
| @@ -82,7 +107,7 @@ module Dependabot | |
| 82 107 |  | 
| 83 108 | 
             
                      old_pin =
         | 
| 84 109 | 
             
                        dependency.previous_requirements
         | 
| 85 | 
            -
                                   | 
| 110 | 
            +
                                  &.find { |r| r[:file] == filename }
         | 
| 86 111 | 
             
                                  &.dig(:source, :ref)
         | 
| 87 112 |  | 
| 88 113 | 
             
                      return content unless old_pin
         | 
| @@ -95,21 +120,31 @@ module Dependabot | |
| 95 120 | 
             
                      )
         | 
| 96 121 | 
             
                    end
         | 
| 97 122 |  | 
| 123 | 
            +
                    sig do
         | 
| 124 | 
            +
                      params(
         | 
| 125 | 
            +
                        content: String,
         | 
| 126 | 
            +
                        dep: Dependabot::Dependency,
         | 
| 127 | 
            +
                        old_req: String,
         | 
| 128 | 
            +
                        new_req: String
         | 
| 129 | 
            +
                      ).returns(String)
         | 
| 130 | 
            +
                    end
         | 
| 98 131 | 
             
                    def update_manifest_req(content:, dep:, old_req:, new_req:)
         | 
| 99 132 | 
             
                      simple_declaration = content.scan(declaration_regex(dep))
         | 
| 100 133 | 
             
                                                  .find { |m| m.include?(old_req) }
         | 
| 101 134 |  | 
| 102 135 | 
             
                      if simple_declaration
         | 
| 103 136 | 
             
                        simple_declaration_regex =
         | 
| 104 | 
            -
                          /(?:^|["'])#{Regexp.escape(simple_declaration)}/
         | 
| 137 | 
            +
                          /(?:^|["'])#{Regexp.escape(T.cast(simple_declaration, String))}/
         | 
| 105 138 | 
             
                        old_req_escaped = Regexp.escape(old_req)
         | 
| 106 139 | 
             
                        content.gsub(simple_declaration_regex) do |line|
         | 
| 107 140 | 
             
                          line.gsub(/.+=.*\K(#{old_req_escaped})/, new_req)
         | 
| 108 141 | 
             
                        end
         | 
| 109 142 | 
             
                      elsif content.match?(feature_declaration_version_regex(dep))
         | 
| 110 143 | 
             
                        content.gsub(feature_declaration_version_regex(dep)) do |part|
         | 
| 111 | 
            -
                           | 
| 112 | 
            -
             | 
| 144 | 
            +
                          match_data = content.match(feature_declaration_version_regex(dep))
         | 
| 145 | 
            +
                          line = T.must(match_data).named_captures.fetch("version_declaration")
         | 
| 146 | 
            +
                          return content if line.nil?
         | 
| 147 | 
            +
             | 
| 113 148 | 
             
                          new_line = line.gsub(old_req, new_req)
         | 
| 114 149 | 
             
                          part.gsub(line, new_line)
         | 
| 115 150 | 
             
                        end
         | 
| @@ -118,20 +153,31 @@ module Dependabot | |
| 118 153 | 
             
                      end
         | 
| 119 154 | 
             
                    end
         | 
| 120 155 |  | 
| 156 | 
            +
                    sig do
         | 
| 157 | 
            +
                      params(
         | 
| 158 | 
            +
                        content: String,
         | 
| 159 | 
            +
                        dep: Dependabot::Dependency,
         | 
| 160 | 
            +
                        old_pin: String,
         | 
| 161 | 
            +
                        new_pin: String
         | 
| 162 | 
            +
                      )
         | 
| 163 | 
            +
                        .returns(String)
         | 
| 164 | 
            +
                    end
         | 
| 121 165 | 
             
                    def update_manifest_pin(content:, dep:, old_pin:, new_pin:)
         | 
| 122 166 | 
             
                      simple_declaration = content.scan(declaration_regex(dep))
         | 
| 123 167 | 
             
                                                  .find { |m| m.include?(old_pin) }
         | 
| 124 168 |  | 
| 125 169 | 
             
                      if simple_declaration
         | 
| 126 170 | 
             
                        simple_declaration_regex =
         | 
| 127 | 
            -
                          /(?:^|["'])#{Regexp.escape(simple_declaration)}/
         | 
| 171 | 
            +
                          /(?:^|["'])#{Regexp.escape(T.cast(simple_declaration, String))}/
         | 
| 128 172 | 
             
                        content.gsub(simple_declaration_regex) do |line|
         | 
| 129 173 | 
             
                          line.gsub(old_pin, new_pin)
         | 
| 130 174 | 
             
                        end
         | 
| 131 175 | 
             
                      elsif content.match?(feature_declaration_pin_regex(dep))
         | 
| 132 176 | 
             
                        content.gsub(feature_declaration_pin_regex(dep)) do |part|
         | 
| 133 | 
            -
                           | 
| 134 | 
            -
             | 
| 177 | 
            +
                          match_data = content.match(feature_declaration_pin_regex(dep))
         | 
| 178 | 
            +
                          line = T.must(match_data).named_captures.fetch("pin_declaration")
         | 
| 179 | 
            +
                          return content if line.nil?
         | 
| 180 | 
            +
             | 
| 135 181 | 
             
                          new_line = line.gsub(old_pin, new_pin)
         | 
| 136 182 | 
             
                          part.gsub(line, new_line)
         | 
| 137 183 | 
             
                        end
         | 
| @@ -140,10 +186,12 @@ module Dependabot | |
| 140 186 | 
             
                      end
         | 
| 141 187 | 
             
                    end
         | 
| 142 188 |  | 
| 189 | 
            +
                    sig { params(dep: Dependabot::Dependency).returns(Regexp) }
         | 
| 143 190 | 
             
                    def declaration_regex(dep)
         | 
| 144 191 | 
             
                      /(?:^|^\s*|["'])#{Regexp.escape(dep.name)}["']?(?:\s*\.version)?\s*=.*$/i
         | 
| 145 192 | 
             
                    end
         | 
| 146 193 |  | 
| 194 | 
            +
                    sig { params(dep: Dependabot::Dependency).returns(Regexp) }
         | 
| 147 195 | 
             
                    def feature_declaration_version_regex(dep)
         | 
| 148 196 | 
             
                      /
         | 
| 149 197 | 
             
                        #{Regexp.quote("dependencies.#{dep.name}]")}
         | 
| @@ -152,6 +200,7 @@ module Dependabot | |
| 152 200 | 
             
                      /mx
         | 
| 153 201 | 
             
                    end
         | 
| 154 202 |  | 
| 203 | 
            +
                    sig { params(dep: Dependabot::Dependency).returns(Regexp) }
         | 
| 155 204 | 
             
                    def feature_declaration_pin_regex(dep)
         | 
| 156 205 | 
             
                      /
         | 
| 157 206 | 
             
                        #{Regexp.quote("dependencies.#{dep.name}]")}
         | 
| @@ -1,7 +1,9 @@ | |
| 1 | 
            -
            # typed:  | 
| 1 | 
            +
            # typed: strict
         | 
| 2 2 | 
             
            # frozen_string_literal: true
         | 
| 3 3 |  | 
| 4 4 | 
             
            require "excon"
         | 
| 5 | 
            +
            require "sorbet-runtime"
         | 
| 6 | 
            +
             | 
| 5 7 | 
             
            require "dependabot/metadata_finders"
         | 
| 6 8 | 
             
            require "dependabot/metadata_finders/base"
         | 
| 7 9 | 
             
            require "dependabot/registry_client"
         | 
| @@ -9,11 +11,20 @@ require "dependabot/registry_client" | |
| 9 11 | 
             
            module Dependabot
         | 
| 10 12 | 
             
              module Cargo
         | 
| 11 13 | 
             
                class MetadataFinder < Dependabot::MetadataFinders::Base
         | 
| 14 | 
            +
                  extend T::Sig
         | 
| 15 | 
            +
             | 
| 12 16 | 
             
                  SOURCE_KEYS = %w(repository homepage documentation).freeze
         | 
| 13 17 | 
             
                  CRATES_IO_API = "https://crates.io/api/v1/crates"
         | 
| 14 18 |  | 
| 19 | 
            +
                  sig { params(dependency: Dependabot::Dependency, credentials: T::Array[Dependabot::Credential]).void }
         | 
| 20 | 
            +
                  def initialize(dependency:, credentials:)
         | 
| 21 | 
            +
                    super
         | 
| 22 | 
            +
                    @crates_listing = T.let(nil, T.nilable(T::Hash[String, T.untyped]))
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
             | 
| 15 25 | 
             
                  private
         | 
| 16 26 |  | 
| 27 | 
            +
                  sig { override.returns(T.nilable(Dependabot::Source)) }
         | 
| 17 28 | 
             
                  def look_up_source
         | 
| 18 29 | 
             
                    case new_source_type
         | 
| 19 30 | 
             
                    when "default" then find_source_from_crates_listing
         | 
| @@ -23,19 +34,22 @@ module Dependabot | |
| 23 34 | 
             
                    end
         | 
| 24 35 | 
             
                  end
         | 
| 25 36 |  | 
| 37 | 
            +
                  sig { returns(T.nilable(String)) }
         | 
| 26 38 | 
             
                  def new_source_type
         | 
| 27 39 | 
             
                    dependency.source_type
         | 
| 28 40 | 
             
                  end
         | 
| 29 41 |  | 
| 42 | 
            +
                  sig { returns(T.nilable(Dependabot::Source)) }
         | 
| 30 43 | 
             
                  def find_source_from_crates_listing
         | 
| 31 44 | 
             
                    potential_source_urls =
         | 
| 32 45 | 
             
                      SOURCE_KEYS
         | 
| 33 | 
            -
                      .filter_map { |key| crates_listing.dig("crate", key) }
         | 
| 46 | 
            +
                      .filter_map { |key| T.must(crates_listing).dig("crate", key) }
         | 
| 34 47 |  | 
| 35 48 | 
             
                    source_url = potential_source_urls.find { |url| Source.from_url(url) }
         | 
| 36 49 | 
             
                    Source.from_url(source_url)
         | 
| 37 50 | 
             
                  end
         | 
| 38 51 |  | 
| 52 | 
            +
                  sig { returns(T.nilable(Dependabot::Source)) }
         | 
| 39 53 | 
             
                  def find_source_from_git_url
         | 
| 40 54 | 
             
                    info = dependency.requirements.filter_map { |r| r[:source] }.first
         | 
| 41 55 |  | 
| @@ -43,6 +57,7 @@ module Dependabot | |
| 43 57 | 
             
                    Source.from_url(url)
         | 
| 44 58 | 
             
                  end
         | 
| 45 59 |  | 
| 60 | 
            +
                  sig { returns(T.nilable(T::Hash[String, T.untyped])) }
         | 
| 46 61 | 
             
                  def crates_listing
         | 
| 47 62 | 
             
                    return @crates_listing unless @crates_listing.nil?
         | 
| 48 63 |  | 
| @@ -56,17 +71,21 @@ module Dependabot | |
| 56 71 | 
             
                    @crates_listing = parse_response(response, index)
         | 
| 57 72 | 
             
                  end
         | 
| 58 73 |  | 
| 74 | 
            +
                  sig { params(index: String, info: T.nilable(T::Hash[String, T.untyped])).returns(T::Hash[String, String]) }
         | 
| 59 75 | 
             
                  def build_headers(index, info)
         | 
| 60 76 | 
             
                    hdrs = { "User-Agent" => "Dependabot (dependabot.com)" }
         | 
| 61 77 | 
             
                    return hdrs if index == CRATES_IO_API
         | 
| 62 78 |  | 
| 63 | 
            -
                     | 
| 79 | 
            +
                    return hdrs if info.nil?
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                    credentials.find { |cred| cred["type"] == "cargo_registry" && cred["registry"] == info["name"] }&.tap do |cred|
         | 
| 64 82 | 
             
                      hdrs["Authorization"] = "Token #{cred['token']}"
         | 
| 65 83 | 
             
                    end
         | 
| 66 84 |  | 
| 67 85 | 
             
                    hdrs
         | 
| 68 86 | 
             
                  end
         | 
| 69 87 |  | 
| 88 | 
            +
                  sig { params(url: String, headers: T::Hash[String, String]).returns(Excon::Response) }
         | 
| 70 89 | 
             
                  def fetch_metadata(url, headers)
         | 
| 71 90 | 
             
                    Excon.get(
         | 
| 72 91 | 
             
                      url,
         | 
| @@ -75,6 +94,7 @@ module Dependabot | |
| 75 94 | 
             
                    )
         | 
| 76 95 | 
             
                  end
         | 
| 77 96 |  | 
| 97 | 
            +
                  sig { params(response: Excon::Response, index: String).returns(T::Hash[String, T.untyped]) }
         | 
| 78 98 | 
             
                  def parse_response(response, index)
         | 
| 79 99 | 
             
                    if index.start_with?("sparse+")
         | 
| 80 100 | 
             
                      parsed_response = response.body.lines.map { |line| JSON.parse(line) }
         | 
| @@ -84,6 +104,7 @@ module Dependabot | |
| 84 104 | 
             
                    end
         | 
| 85 105 | 
             
                  end
         | 
| 86 106 |  | 
| 107 | 
            +
                  sig { params(dependency: Dependabot::Dependency, index: String).returns(String) }
         | 
| 87 108 | 
             
                  def metadata_fetch_url(dependency, index)
         | 
| 88 109 | 
             
                    return "#{index}/#{dependency.name}" if index == CRATES_IO_API
         | 
| 89 110 |  | 
| @@ -89,7 +89,7 @@ module Dependabot | |
| 89 89 | 
             
                    return req_string if parts.count >= 3
         | 
| 90 90 |  | 
| 91 91 | 
             
                    # If we have no parts then the version is completely unlocked
         | 
| 92 | 
            -
                    return ">= 0" if parts. | 
| 92 | 
            +
                    return ">= 0" if parts.none?
         | 
| 93 93 |  | 
| 94 94 | 
             
                    # If we have fewer than three parts we do a partial match
         | 
| 95 95 | 
             
                    parts << "0"
         | 
| @@ -1,7 +1,9 @@ | |
| 1 | 
            -
            # typed:  | 
| 1 | 
            +
            # typed: strict
         | 
| 2 2 | 
             
            # frozen_string_literal: true
         | 
| 3 3 |  | 
| 4 | 
            +
            require "sorbet-runtime"
         | 
| 4 5 | 
             
            require "toml-rb"
         | 
| 6 | 
            +
             | 
| 5 7 | 
             
            require "dependabot/dependency_file"
         | 
| 6 8 | 
             
            require "dependabot/cargo/file_parser"
         | 
| 7 9 | 
             
            require "dependabot/cargo/update_checker"
         | 
| @@ -12,6 +14,18 @@ module Dependabot | |
| 12 14 | 
             
                  # This class takes a set of dependency files and sanitizes them for use
         | 
| 13 15 | 
             
                  # in UpdateCheckers::Rust::Cargo.
         | 
| 14 16 | 
             
                  class FilePreparer
         | 
| 17 | 
            +
                    extend T::Sig
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                    sig do
         | 
| 20 | 
            +
                      params(
         | 
| 21 | 
            +
                        dependency_files: T::Array[Dependabot::DependencyFile],
         | 
| 22 | 
            +
                        dependency: Dependabot::Dependency,
         | 
| 23 | 
            +
                        unlock_requirement: T::Boolean,
         | 
| 24 | 
            +
                        replacement_git_pin: T.nilable(String),
         | 
| 25 | 
            +
                        latest_allowable_version: T.nilable(T.any(String, Gem::Version))
         | 
| 26 | 
            +
                      )
         | 
| 27 | 
            +
                        .void
         | 
| 28 | 
            +
                    end
         | 
| 15 29 | 
             
                    def initialize(dependency_files:, dependency:,
         | 
| 16 30 | 
             
                                   unlock_requirement: true,
         | 
| 17 31 | 
             
                                   replacement_git_pin: nil,
         | 
| @@ -21,8 +35,13 @@ module Dependabot | |
| 21 35 | 
             
                      @unlock_requirement       = unlock_requirement
         | 
| 22 36 | 
             
                      @replacement_git_pin      = replacement_git_pin
         | 
| 23 37 | 
             
                      @latest_allowable_version = latest_allowable_version
         | 
| 38 | 
            +
                      @lower_bound_version = T.let(nil, T.nilable(T.any(String, Integer)))
         | 
| 39 | 
            +
                      @manifest_files = T.let(nil, T.nilable(T::Array[Dependabot::DependencyFile]))
         | 
| 40 | 
            +
                      @lockfile = T.let(nil, T.nilable(Dependabot::DependencyFile))
         | 
| 41 | 
            +
                      @toolchain = T.let(nil, T.nilable(Dependabot::DependencyFile))
         | 
| 24 42 | 
             
                    end
         | 
| 25 43 |  | 
| 44 | 
            +
                    sig { returns(T::Array[Dependabot::DependencyFile]) }
         | 
| 26 45 | 
             
                    def prepared_dependency_files
         | 
| 27 46 | 
             
                      files = []
         | 
| 28 47 | 
             
                      files += manifest_files.map do |file|
         | 
| @@ -39,21 +58,31 @@ module Dependabot | |
| 39 58 |  | 
| 40 59 | 
             
                    private
         | 
| 41 60 |  | 
| 61 | 
            +
                    sig { returns(T::Array[Dependabot::DependencyFile]) }
         | 
| 42 62 | 
             
                    attr_reader :dependency_files
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                    sig { returns(Dependabot::Dependency) }
         | 
| 43 65 | 
             
                    attr_reader :dependency
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                    sig { returns(T.nilable(String)) }
         | 
| 44 68 | 
             
                    attr_reader :replacement_git_pin
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                    sig { returns(T.nilable(T.any(String, Gem::Version))) }
         | 
| 45 71 | 
             
                    attr_reader :latest_allowable_version
         | 
| 46 72 |  | 
| 73 | 
            +
                    sig { returns(T::Boolean) }
         | 
| 47 74 | 
             
                    def unlock_requirement?
         | 
| 48 75 | 
             
                      @unlock_requirement
         | 
| 49 76 | 
             
                    end
         | 
| 50 77 |  | 
| 78 | 
            +
                    sig { returns(T::Boolean) }
         | 
| 51 79 | 
             
                    def replace_git_pin?
         | 
| 52 80 | 
             
                      !replacement_git_pin.nil?
         | 
| 53 81 | 
             
                    end
         | 
| 54 82 |  | 
| 83 | 
            +
                    sig { params(file: Dependabot::DependencyFile).returns(String) }
         | 
| 55 84 | 
             
                    def manifest_content_for_update_check(file)
         | 
| 56 | 
            -
                      content = file.content
         | 
| 85 | 
            +
                      content = T.must(file.content)
         | 
| 57 86 |  | 
| 58 87 | 
             
                      unless file.support_file?
         | 
| 59 88 | 
             
                        content = replace_version_constraint(content, file.name)
         | 
| @@ -67,6 +96,7 @@ module Dependabot | |
| 67 96 |  | 
| 68 97 | 
             
                    # NOTE: We don't need to care about formatting in this method, since
         | 
| 69 98 | 
             
                    # we're only using the manifest to find the latest resolvable version
         | 
| 99 | 
            +
                    sig { params(content: String, filename: String).returns(String) }
         | 
| 70 100 | 
             
                    def replace_version_constraint(content, filename)
         | 
| 71 101 | 
             
                      parsed_manifest = TomlRB.parse(content)
         | 
| 72 102 |  | 
| @@ -89,6 +119,7 @@ module Dependabot | |
| 89 119 | 
             
                      TomlRB.dump(parsed_manifest)
         | 
| 90 120 | 
             
                    end
         | 
| 91 121 |  | 
| 122 | 
            +
                    sig { params(parsed_manifest: T::Hash[String, T.untyped], filename: String).void }
         | 
| 92 123 | 
             
                    def replace_req_on_target_specific_deps!(parsed_manifest, filename)
         | 
| 93 124 | 
             
                      parsed_manifest.fetch("target", {}).each do |target, _|
         | 
| 94 125 | 
             
                        Cargo::FileParser::DEPENDENCY_TYPES.each do |type|
         | 
| @@ -114,6 +145,7 @@ module Dependabot | |
| 114 145 | 
             
                      end
         | 
| 115 146 | 
             
                    end
         | 
| 116 147 |  | 
| 148 | 
            +
                    sig { params(content: String).returns(String) }
         | 
| 117 149 | 
             
                    def replace_git_pin(content)
         | 
| 118 150 | 
             
                      parsed_manifest = TomlRB.parse(content)
         | 
| 119 151 |  | 
| @@ -121,7 +153,7 @@ module Dependabot | |
| 121 153 | 
             
                        dependency_names_for_type(parsed_manifest, type).each do |name|
         | 
| 122 154 | 
             
                          req = parsed_manifest.dig(type, name)
         | 
| 123 155 | 
             
                          next unless req.is_a?(Hash)
         | 
| 124 | 
            -
                          next unless [req["tag"], req["rev"]].compact.uniq. | 
| 156 | 
            +
                          next unless [req["tag"], req["rev"]].compact.uniq.one?
         | 
| 125 157 |  | 
| 126 158 | 
             
                          parsed_manifest[type][name]["tag"] = replacement_git_pin if req["tag"]
         | 
| 127 159 |  | 
| @@ -134,6 +166,7 @@ module Dependabot | |
| 134 166 | 
             
                      TomlRB.dump(parsed_manifest)
         | 
| 135 167 | 
             
                    end
         | 
| 136 168 |  | 
| 169 | 
            +
                    sig { params(parsed_manifest: T::Hash[String, T.untyped]).void }
         | 
| 137 170 | 
             
                    def replace_git_pin_on_target_specific_deps!(parsed_manifest)
         | 
| 138 171 | 
             
                      parsed_manifest.fetch("target", {}).each do |target, _|
         | 
| 139 172 | 
             
                        Cargo::FileParser::DEPENDENCY_TYPES.each do |type|
         | 
| @@ -146,7 +179,7 @@ module Dependabot | |
| 146 179 | 
             
                          dependency_names.each do |name|
         | 
| 147 180 | 
             
                            req = parsed_manifest.dig("target", target, type, name)
         | 
| 148 181 | 
             
                            next unless req.is_a?(Hash)
         | 
| 149 | 
            -
                            next unless [req["tag"], req["rev"]].compact.uniq. | 
| 182 | 
            +
                            next unless [req["tag"], req["rev"]].compact.uniq.one?
         | 
| 150 183 |  | 
| 151 184 | 
             
                            if req["tag"]
         | 
| 152 185 | 
             
                              parsed_manifest["target"][target][type][name]["tag"] =
         | 
| @@ -162,6 +195,7 @@ module Dependabot | |
| 162 195 | 
             
                      end
         | 
| 163 196 | 
             
                    end
         | 
| 164 197 |  | 
| 198 | 
            +
                    sig { params(content: String).returns(String) }
         | 
| 165 199 | 
             
                    def replace_ssh_urls(content)
         | 
| 166 200 | 
             
                      parsed_manifest = TomlRB.parse(content)
         | 
| 167 201 |  | 
| @@ -178,6 +212,7 @@ module Dependabot | |
| 178 212 | 
             
                      TomlRB.dump(parsed_manifest)
         | 
| 179 213 | 
             
                    end
         | 
| 180 214 |  | 
| 215 | 
            +
                    sig { params(filename: String).returns(String) }
         | 
| 181 216 | 
             
                    def temporary_requirement_for_resolution(filename)
         | 
| 182 217 | 
             
                      original_req = dependency.requirements
         | 
| 183 218 | 
             
                                               .find { |r| r.fetch(:file) == filename }
         | 
| @@ -201,6 +236,7 @@ module Dependabot | |
| 201 236 | 
             
                    end
         | 
| 202 237 |  | 
| 203 238 | 
             
                    # rubocop:disable Metrics/PerceivedComplexity
         | 
| 239 | 
            +
                    sig { returns(T.any(String, Integer)) }
         | 
| 204 240 | 
             
                    def lower_bound_version
         | 
| 205 241 | 
             
                      @lower_bound_version ||=
         | 
| 206 242 | 
             
                        if git_dependency? && git_dependency_version
         | 
| @@ -221,16 +257,18 @@ module Dependabot | |
| 221 257 | 
             
                    end
         | 
| 222 258 | 
             
                    # rubocop:enable Metrics/PerceivedComplexity
         | 
| 223 259 |  | 
| 260 | 
            +
                    sig { returns(T.nilable(String)) }
         | 
| 224 261 | 
             
                    def git_dependency_version
         | 
| 225 262 | 
             
                      return unless lockfile
         | 
| 226 263 |  | 
| 227 | 
            -
                      TomlRB.parse(lockfile.content)
         | 
| 264 | 
            +
                      TomlRB.parse(T.must(lockfile).content)
         | 
| 228 265 | 
             
                            .fetch("package", [])
         | 
| 229 266 | 
             
                            .select { |p| p["name"] == dependency.name }
         | 
| 230 267 | 
             
                            .find { |p| p["source"].end_with?(dependency.version) }
         | 
| 231 268 | 
             
                            .fetch("version")
         | 
| 232 269 | 
             
                    end
         | 
| 233 270 |  | 
| 271 | 
            +
                    sig { params(parsed_manifest: T::Hash[String, T.untyped], type: String).returns(T::Array[String]) }
         | 
| 234 272 | 
             
                    def dependency_names_for_type(parsed_manifest, type)
         | 
| 235 273 | 
             
                      names = []
         | 
| 236 274 | 
             
                      parsed_manifest.fetch(type, {}).each do |nm, req|
         | 
| @@ -241,6 +279,9 @@ module Dependabot | |
| 241 279 | 
             
                      names
         | 
| 242 280 | 
             
                    end
         | 
| 243 281 |  | 
| 282 | 
            +
                    sig do
         | 
| 283 | 
            +
                      params(parsed_manifest: T::Hash[String, T.untyped], type: String, target: String).returns(T::Array[String])
         | 
| 284 | 
            +
                    end
         | 
| 244 285 | 
             
                    def dependency_names_for_type_and_target(parsed_manifest, type, target)
         | 
| 245 286 | 
             
                      names = []
         | 
| 246 287 | 
             
                      (parsed_manifest.dig("target", target, type) || {}).each do |nm, req|
         | 
| @@ -251,13 +292,16 @@ module Dependabot | |
| 251 292 | 
             
                      names
         | 
| 252 293 | 
             
                    end
         | 
| 253 294 |  | 
| 295 | 
            +
                    sig { params(name: String, declaration: T.untyped).returns(String) }
         | 
| 254 296 | 
             
                    def name_from_declaration(name, declaration)
         | 
| 255 297 | 
             
                      return name if declaration.is_a?(String)
         | 
| 298 | 
            +
             | 
| 256 299 | 
             
                      raise "Unexpected dependency declaration: #{declaration}" unless declaration.is_a?(Hash)
         | 
| 257 300 |  | 
| 258 301 | 
             
                      declaration.fetch("package", name)
         | 
| 259 302 | 
             
                    end
         | 
| 260 303 |  | 
| 304 | 
            +
                    sig { returns(T::Array[Dependabot::DependencyFile]) }
         | 
| 261 305 | 
             
                    def manifest_files
         | 
| 262 306 | 
             
                      @manifest_files ||=
         | 
| 263 307 | 
             
                        dependency_files.select { |f| f.name.end_with?("Cargo.toml") }
         | 
| @@ -267,15 +311,18 @@ module Dependabot | |
| 267 311 | 
             
                      @manifest_files
         | 
| 268 312 | 
             
                    end
         | 
| 269 313 |  | 
| 314 | 
            +
                    sig { returns(T.nilable(Dependabot::DependencyFile)) }
         | 
| 270 315 | 
             
                    def lockfile
         | 
| 271 316 | 
             
                      @lockfile ||= dependency_files.find { |f| f.name == "Cargo.lock" }
         | 
| 272 317 | 
             
                    end
         | 
| 273 318 |  | 
| 319 | 
            +
                    sig { returns(T.nilable(Dependabot::DependencyFile)) }
         | 
| 274 320 | 
             
                    def toolchain
         | 
| 275 321 | 
             
                      @toolchain ||=
         | 
| 276 322 | 
             
                        dependency_files.find { |f| f.name == "rust-toolchain" }
         | 
| 277 323 | 
             
                    end
         | 
| 278 324 |  | 
| 325 | 
            +
                    sig { returns(T::Boolean) }
         | 
| 279 326 | 
             
                    def git_dependency?
         | 
| 280 327 | 
             
                      GitCommitChecker
         | 
| 281 328 | 
             
                        .new(dependency: dependency, credentials: [])
         | 
| @@ -1,4 +1,4 @@ | |
| 1 | 
            -
            # typed:  | 
| 1 | 
            +
            # typed: strict
         | 
| 2 2 | 
             
            # frozen_string_literal: true
         | 
| 3 3 |  | 
| 4 4 | 
             
            ################################################################################
         | 
| @@ -25,26 +25,35 @@ module Dependabot | |
| 25 25 | 
             
                    VERSION_REGEX = /[0-9]+(?:\.[A-Za-z0-9\-*]+)*/
         | 
| 26 26 | 
             
                    ALLOWED_UPDATE_STRATEGIES = T.let(
         | 
| 27 27 | 
             
                      [
         | 
| 28 | 
            -
                        RequirementsUpdateStrategy::LockfileOnly,
         | 
| 29 | 
            -
                        RequirementsUpdateStrategy::BumpVersions,
         | 
| 30 | 
            -
                        RequirementsUpdateStrategy::BumpVersionsIfNecessary
         | 
| 28 | 
            +
                        Dependabot::RequirementsUpdateStrategy::LockfileOnly,
         | 
| 29 | 
            +
                        Dependabot::RequirementsUpdateStrategy::BumpVersions,
         | 
| 30 | 
            +
                        Dependabot::RequirementsUpdateStrategy::BumpVersionsIfNecessary
         | 
| 31 31 | 
             
                      ].freeze,
         | 
| 32 32 | 
             
                      T::Array[Dependabot::RequirementsUpdateStrategy]
         | 
| 33 33 | 
             
                    )
         | 
| 34 34 |  | 
| 35 | 
            +
                    sig do
         | 
| 36 | 
            +
                      params(
         | 
| 37 | 
            +
                        requirements: T::Array[T::Hash[Symbol, T.untyped]],
         | 
| 38 | 
            +
                        updated_source: T.nilable(T::Hash[T.any(String, Symbol), T.untyped]),
         | 
| 39 | 
            +
                        update_strategy: Dependabot::RequirementsUpdateStrategy,
         | 
| 40 | 
            +
                        target_version: T.nilable(T.any(String, Gem::Version))
         | 
| 41 | 
            +
                      ).void
         | 
| 42 | 
            +
                    end
         | 
| 35 43 | 
             
                    def initialize(requirements:, updated_source:, update_strategy:,
         | 
| 36 44 | 
             
                                   target_version:)
         | 
| 37 | 
            -
                      @requirements = requirements
         | 
| 38 | 
            -
                      @updated_source = updated_source
         | 
| 39 | 
            -
                      @update_strategy = update_strategy
         | 
| 45 | 
            +
                      @requirements = T.let(requirements, T::Array[T::Hash[Symbol, T.untyped]])
         | 
| 46 | 
            +
                      @updated_source = T.let(updated_source, T.nilable(T::Hash[T.any(String, Symbol), T.untyped]))
         | 
| 47 | 
            +
                      @update_strategy = T.let(update_strategy, Dependabot::RequirementsUpdateStrategy)
         | 
| 40 48 |  | 
| 41 49 | 
             
                      check_update_strategy
         | 
| 42 50 |  | 
| 43 51 | 
             
                      return unless target_version && version_class.correct?(target_version)
         | 
| 44 52 |  | 
| 45 | 
            -
                      @target_version = version_class.new(target_version)
         | 
| 53 | 
            +
                      @target_version = T.let(version_class.new(target_version), Gem::Version)
         | 
| 46 54 | 
             
                    end
         | 
| 47 55 |  | 
| 56 | 
            +
                    sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) }
         | 
| 48 57 | 
             
                    def updated_requirements
         | 
| 49 58 | 
             
                      return requirements if update_strategy.lockfile_only?
         | 
| 50 59 |  | 
| @@ -57,7 +66,7 @@ module Dependabot | |
| 57 66 | 
             
                        next req if req[:requirement].nil?
         | 
| 58 67 |  | 
| 59 68 | 
             
                        # TODO: Add a RequirementsUpdateStrategy::WidenRanges options
         | 
| 60 | 
            -
                        if update_strategy == RequirementsUpdateStrategy::BumpVersionsIfNecessary
         | 
| 69 | 
            +
                        if update_strategy == Dependabot::RequirementsUpdateStrategy::BumpVersionsIfNecessary
         | 
| 61 70 | 
             
                          update_version_requirement_if_needed(req)
         | 
| 62 71 | 
             
                        else
         | 
| 63 72 | 
             
                          update_version_requirement(req)
         | 
| @@ -67,17 +76,26 @@ module Dependabot | |
| 67 76 |  | 
| 68 77 | 
             
                    private
         | 
| 69 78 |  | 
| 79 | 
            +
                    sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) }
         | 
| 70 80 | 
             
                    attr_reader :requirements
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                    sig { returns(T.nilable(T::Hash[T.any(String, Symbol), T.untyped])) }
         | 
| 71 83 | 
             
                    attr_reader :updated_source
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                    sig { returns(Dependabot::RequirementsUpdateStrategy) }
         | 
| 72 86 | 
             
                    attr_reader :update_strategy
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                    sig { returns(T.nilable(Gem::Version)) }
         | 
| 73 89 | 
             
                    attr_reader :target_version
         | 
| 74 90 |  | 
| 91 | 
            +
                    sig { void }
         | 
| 75 92 | 
             
                    def check_update_strategy
         | 
| 76 93 | 
             
                      return if ALLOWED_UPDATE_STRATEGIES.include?(update_strategy)
         | 
| 77 94 |  | 
| 78 95 | 
             
                      raise "Unknown update strategy: #{update_strategy}"
         | 
| 79 96 | 
             
                    end
         | 
| 80 97 |  | 
| 98 | 
            +
                    sig { params(req: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
         | 
| 81 99 | 
             
                    def update_version_requirement(req)
         | 
| 82 100 | 
             
                      string_reqs = req[:requirement].split(",").map(&:strip)
         | 
| 83 101 |  | 
| @@ -100,15 +118,17 @@ module Dependabot | |
| 100 118 | 
             
                      req.merge(requirement: new_requirement)
         | 
| 101 119 | 
             
                    end
         | 
| 102 120 |  | 
| 121 | 
            +
                    sig { params(req: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
         | 
| 103 122 | 
             
                    def update_version_requirement_if_needed(req)
         | 
| 104 123 | 
             
                      string_reqs = req[:requirement].split(",").map(&:strip)
         | 
| 105 | 
            -
                      ruby_reqs = string_reqs.map { |r| Cargo::Requirement.new(r) }
         | 
| 124 | 
            +
                      ruby_reqs = string_reqs.map { |r| Dependabot::Cargo::Requirement.new(r) }
         | 
| 106 125 |  | 
| 107 126 | 
             
                      return req if ruby_reqs.all? { |r| r.satisfied_by?(target_version) }
         | 
| 108 127 |  | 
| 109 128 | 
             
                      update_version_requirement(req)
         | 
| 110 129 | 
             
                    end
         | 
| 111 130 |  | 
| 131 | 
            +
                    sig { params(req_string: String).returns(String) }
         | 
| 112 132 | 
             
                    def update_version_string(req_string)
         | 
| 113 133 | 
             
                      new_target_parts = target_version.to_s.sub(/\+.*/, "").split(".")
         | 
| 114 134 | 
             
                      req_string.sub(VERSION_REGEX) do |old_version|
         | 
| @@ -123,19 +143,22 @@ module Dependabot | |
| 123 143 | 
             
                      end
         | 
| 124 144 | 
             
                    end
         | 
| 125 145 |  | 
| 146 | 
            +
                    sig { params(string_reqs: T::Array[String]).returns(T.nilable(String)) }
         | 
| 126 147 | 
             
                    def non_range_req(string_reqs)
         | 
| 127 148 | 
             
                      string_reqs.find { |r| r.include?("*") || r.match?(/^[\d~^]/) }
         | 
| 128 149 | 
             
                    end
         | 
| 129 150 |  | 
| 151 | 
            +
                    sig { params(string_reqs: T::Array[String]).returns(T.nilable(String)) }
         | 
| 130 152 | 
             
                    def exact_req(string_reqs)
         | 
| 131 | 
            -
                      string_reqs.find { |r| Cargo::Requirement.new(r).exact? }
         | 
| 153 | 
            +
                      string_reqs.find { |r| Dependabot::Cargo::Requirement.new(r).exact? }
         | 
| 132 154 | 
             
                    end
         | 
| 133 155 |  | 
| 156 | 
            +
                    sig { params(string_reqs: T::Array[String]).returns(T.any(String, Symbol)) }
         | 
| 134 157 | 
             
                    def update_range_requirements(string_reqs)
         | 
| 135 158 | 
             
                      string_reqs.map do |req|
         | 
| 136 159 | 
             
                        next req unless req.match?(/[<>]/)
         | 
| 137 160 |  | 
| 138 | 
            -
                        ruby_req = Cargo::Requirement.new(req)
         | 
| 161 | 
            +
                        ruby_req = Dependabot::Cargo::Requirement.new(req)
         | 
| 139 162 | 
             
                        next req if ruby_req.satisfied_by?(target_version)
         | 
| 140 163 |  | 
| 141 164 | 
             
                        raise UnfixableRequirement if req.start_with?(">")
         | 
| @@ -144,7 +167,7 @@ module Dependabot | |
| 144 167 | 
             
                          if req.start_with?("<=")
         | 
| 145 168 | 
             
                            update_version_string(old_version)
         | 
| 146 169 | 
             
                          else
         | 
| 147 | 
            -
                            update_greatest_version(old_version, target_version)
         | 
| 170 | 
            +
                            update_greatest_version(old_version, T.must(target_version))
         | 
| 148 171 | 
             
                          end
         | 
| 149 172 | 
             
                        end
         | 
| 150 173 | 
             
                      end.join(", ")
         | 
| @@ -152,26 +175,28 @@ module Dependabot | |
| 152 175 | 
             
                      :unfixable
         | 
| 153 176 | 
             
                    end
         | 
| 154 177 |  | 
| 178 | 
            +
                    sig { params(old_version: String, version_to_be_permitted: Gem::Version).returns(String) }
         | 
| 155 179 | 
             
                    def update_greatest_version(old_version, version_to_be_permitted)
         | 
| 156 180 | 
             
                      version = version_class.new(old_version)
         | 
| 157 181 | 
             
                      version = version.release if version.prerelease?
         | 
| 158 182 |  | 
| 159 183 | 
             
                      index_to_update =
         | 
| 160 | 
            -
                        version.segments.map.with_index { |seg, i| seg.zero? ? 0 : i }.max
         | 
| 184 | 
            +
                        version.segments.map.with_index { |seg, i| seg.to_i.zero? ? 0 : i }.max
         | 
| 161 185 |  | 
| 162 186 | 
             
                      version.segments.map.with_index do |_, index|
         | 
| 163 187 | 
             
                        if index < index_to_update
         | 
| 164 188 | 
             
                          version_to_be_permitted.segments[index]
         | 
| 165 189 | 
             
                        elsif index == index_to_update
         | 
| 166 | 
            -
                          version_to_be_permitted.segments[index] + 1
         | 
| 190 | 
            +
                          version_to_be_permitted.segments[index].to_i + 1
         | 
| 167 191 | 
             
                        else
         | 
| 168 192 | 
             
                          0
         | 
| 169 193 | 
             
                        end
         | 
| 170 194 | 
             
                      end.join(".")
         | 
| 171 195 | 
             
                    end
         | 
| 172 196 |  | 
| 197 | 
            +
                    sig { returns(T.class_of(Dependabot::Cargo::Version)) }
         | 
| 173 198 | 
             
                    def version_class
         | 
| 174 | 
            -
                      Cargo::Version
         | 
| 199 | 
            +
                      Dependabot::Cargo::Version
         | 
| 175 200 | 
             
                    end
         | 
| 176 201 | 
             
                  end
         | 
| 177 202 | 
             
                end
         | 
| @@ -14,6 +14,7 @@ module Dependabot | |
| 14 14 | 
             
                class UpdateChecker
         | 
| 15 15 | 
             
                  class VersionResolver # rubocop:disable Metrics/ClassLength
         | 
| 16 16 | 
             
                    extend T::Sig
         | 
| 17 | 
            +
             | 
| 17 18 | 
             
                    UNABLE_TO_UPDATE = /Unable to update (?<url>.*?)$/
         | 
| 18 19 | 
             
                    BRANCH_NOT_FOUND_REGEX = /#{UNABLE_TO_UPDATE}.*to find branch `(?<branch>[^`]+)`/m
         | 
| 19 20 | 
             
                    REVSPEC_PATTERN = /revspec '.*' not found/
         | 
| @@ -133,7 +134,7 @@ module Dependabot | |
| 133 134 | 
             
                              dependency.version
         | 
| 134 135 | 
             
                            end
         | 
| 135 136 |  | 
| 136 | 
            -
                      if spec_options. | 
| 137 | 
            +
                      if spec_options.one? { |s| s.end_with?(T.must(ver)) }
         | 
| 137 138 | 
             
                        @custom_specification = spec_options.find { |s| s.end_with?(T.must(ver)) }
         | 
| 138 139 | 
             
                        return true
         | 
| 139 140 | 
             
                      elsif spec_options.count { |s| s.end_with?(T.must(ver)) } > 1
         | 
| @@ -1,6 +1,8 @@ | |
| 1 | 
            -
            # typed:  | 
| 1 | 
            +
            # typed: strict
         | 
| 2 2 | 
             
            # frozen_string_literal: true
         | 
| 3 3 |  | 
| 4 | 
            +
            require "sorbet-runtime"
         | 
| 5 | 
            +
             | 
| 4 6 | 
             
            require "dependabot/git_commit_checker"
         | 
| 5 7 | 
             
            require "dependabot/requirements_update_strategy"
         | 
| 6 8 | 
             
            require "dependabot/update_checkers"
         | 
| @@ -9,15 +11,18 @@ require "dependabot/update_checkers/base" | |
| 9 11 | 
             
            module Dependabot
         | 
| 10 12 | 
             
              module Cargo
         | 
| 11 13 | 
             
                class UpdateChecker < Dependabot::UpdateCheckers::Base
         | 
| 14 | 
            +
                  extend T::Sig
         | 
| 15 | 
            +
             | 
| 12 16 | 
             
                  require_relative "update_checker/latest_version_finder"
         | 
| 13 17 | 
             
                  require_relative "update_checker/requirements_updater"
         | 
| 14 18 | 
             
                  require_relative "update_checker/version_resolver"
         | 
| 15 19 | 
             
                  require_relative "update_checker/file_preparer"
         | 
| 16 20 |  | 
| 21 | 
            +
                  sig { override.returns(T.nilable(T.any(String, Gem::Version))) }
         | 
| 17 22 | 
             
                  def latest_version
         | 
| 18 23 | 
             
                    return if path_dependency?
         | 
| 19 24 |  | 
| 20 | 
            -
                    @latest_version =
         | 
| 25 | 
            +
                    @latest_version = T.let(
         | 
| 21 26 | 
             
                      if git_dependency?
         | 
| 22 27 | 
             
                        latest_version_for_git_dependency
         | 
| 23 28 | 
             
                      elsif git_subdependency?
         | 
| @@ -26,48 +31,64 @@ module Dependabot | |
| 26 31 | 
             
                        nil
         | 
| 27 32 | 
             
                      else
         | 
| 28 33 | 
             
                        latest_version_finder.latest_version
         | 
| 29 | 
            -
                      end
         | 
| 34 | 
            +
                      end,
         | 
| 35 | 
            +
                      T.nilable(T.any(String, Gem::Version))
         | 
| 36 | 
            +
                    )
         | 
| 30 37 | 
             
                  end
         | 
| 31 38 |  | 
| 39 | 
            +
                  sig { override.returns(T.nilable(T.any(String, Gem::Version))) }
         | 
| 32 40 | 
             
                  def latest_resolvable_version
         | 
| 33 41 | 
             
                    return if path_dependency?
         | 
| 34 42 |  | 
| 35 | 
            -
                    @latest_resolvable_version  | 
| 36 | 
            -
                       | 
| 37 | 
            -
                         | 
| 38 | 
            -
             | 
| 39 | 
            -
                         | 
| 40 | 
            -
             | 
| 41 | 
            -
             | 
| 42 | 
            -
             | 
| 43 | 
            -
                         | 
| 44 | 
            -
             | 
| 43 | 
            +
                    @latest_resolvable_version = T.let(
         | 
| 44 | 
            +
                      @latest_resolvable_version ||
         | 
| 45 | 
            +
                        if git_dependency?
         | 
| 46 | 
            +
                          latest_resolvable_version_for_git_dependency
         | 
| 47 | 
            +
                        elsif git_subdependency?
         | 
| 48 | 
            +
                          # TODO: Dependabot can't update git sub-dependencies yet, because
         | 
| 49 | 
            +
                          # they can't be passed to GitCommitChecker.
         | 
| 50 | 
            +
                          nil
         | 
| 51 | 
            +
                        else
         | 
| 52 | 
            +
                          fetch_latest_resolvable_version(unlock_requirement: true)
         | 
| 53 | 
            +
                        end,
         | 
| 54 | 
            +
                      T.nilable(T.any(String, Gem::Version))
         | 
| 55 | 
            +
                    )
         | 
| 45 56 | 
             
                  end
         | 
| 46 57 |  | 
| 58 | 
            +
                  sig { override.returns(T.nilable(Gem::Version)) }
         | 
| 47 59 | 
             
                  def lowest_security_fix_version
         | 
| 48 60 | 
             
                    latest_version_finder.lowest_security_fix_version
         | 
| 49 61 | 
             
                  end
         | 
| 50 62 |  | 
| 63 | 
            +
                  sig { override.returns(T.nilable(Gem::Version)) }
         | 
| 51 64 | 
             
                  def lowest_resolvable_security_fix_version
         | 
| 52 65 | 
             
                    raise "Dependency not vulnerable!" unless vulnerable?
         | 
| 53 66 |  | 
| 54 67 | 
             
                    return @lowest_resolvable_security_fix_version if defined?(@lowest_resolvable_security_fix_version)
         | 
| 55 68 |  | 
| 56 | 
            -
                     | 
| 57 | 
            -
             | 
| 69 | 
            +
                    result = fetch_lowest_resolvable_security_fix_version
         | 
| 70 | 
            +
                    @lowest_resolvable_security_fix_version = T.let(
         | 
| 71 | 
            +
                      result.is_a?(Gem::Version) ? result : nil,
         | 
| 72 | 
            +
                      T.nilable(Gem::Version)
         | 
| 73 | 
            +
                    )
         | 
| 58 74 | 
             
                  end
         | 
| 59 75 |  | 
| 76 | 
            +
                  sig { override.returns(T.nilable(T.any(String, Gem::Version))) }
         | 
| 60 77 | 
             
                  def latest_resolvable_version_with_no_unlock
         | 
| 61 78 | 
             
                    return if path_dependency?
         | 
| 62 79 |  | 
| 63 | 
            -
                    @latest_resolvable_version_with_no_unlock  | 
| 64 | 
            -
                       | 
| 65 | 
            -
                         | 
| 66 | 
            -
             | 
| 67 | 
            -
                         | 
| 68 | 
            -
             | 
| 80 | 
            +
                    @latest_resolvable_version_with_no_unlock = T.let(
         | 
| 81 | 
            +
                      @latest_resolvable_version_with_no_unlock ||
         | 
| 82 | 
            +
                        if git_dependency?
         | 
| 83 | 
            +
                          latest_resolvable_commit_with_unchanged_git_source
         | 
| 84 | 
            +
                        else
         | 
| 85 | 
            +
                          fetch_latest_resolvable_version(unlock_requirement: false)
         | 
| 86 | 
            +
                        end,
         | 
| 87 | 
            +
                      T.nilable(T.any(String, Gem::Version))
         | 
| 88 | 
            +
                    )
         | 
| 69 89 | 
             
                  end
         | 
| 70 90 |  | 
| 91 | 
            +
                  sig { override.returns(T::Array[T::Hash[Symbol, T.untyped]]) }
         | 
| 71 92 | 
             
                  def updated_requirements
         | 
| 72 93 | 
             
                    RequirementsUpdater.new(
         | 
| 73 94 | 
             
                      requirements: dependency.requirements,
         | 
| @@ -77,10 +98,12 @@ module Dependabot | |
| 77 98 | 
             
                    ).updated_requirements
         | 
| 78 99 | 
             
                  end
         | 
| 79 100 |  | 
| 101 | 
            +
                  sig { override.returns(T::Boolean) }
         | 
| 80 102 | 
             
                  def requirements_unlocked_or_can_be?
         | 
| 81 103 | 
             
                    !requirements_update_strategy.lockfile_only?
         | 
| 82 104 | 
             
                  end
         | 
| 83 105 |  | 
| 106 | 
            +
                  sig { returns(Dependabot::RequirementsUpdateStrategy) }
         | 
| 84 107 | 
             
                  def requirements_update_strategy
         | 
| 85 108 | 
             
                    # If passed in as an option (in the base class) honour that option
         | 
| 86 109 | 
             
                    return @requirements_update_strategy if @requirements_update_strategy
         | 
| @@ -91,15 +114,18 @@ module Dependabot | |
| 91 114 |  | 
| 92 115 | 
             
                  private
         | 
| 93 116 |  | 
| 117 | 
            +
                  sig { override.returns(T::Boolean) }
         | 
| 94 118 | 
             
                  def latest_version_resolvable_with_full_unlock?
         | 
| 95 119 | 
             
                    # Full unlock checks aren't implemented for Rust (yet)
         | 
| 96 120 | 
             
                    false
         | 
| 97 121 | 
             
                  end
         | 
| 98 122 |  | 
| 123 | 
            +
                  sig { override.returns(T::Array[Dependabot::Dependency]) }
         | 
| 99 124 | 
             
                  def updated_dependencies_after_full_unlock
         | 
| 100 125 | 
             
                    raise NotImplementedError
         | 
| 101 126 | 
             
                  end
         | 
| 102 127 |  | 
| 128 | 
            +
                  sig { returns(T.nilable(String)) }
         | 
| 103 129 | 
             
                  def target_version
         | 
| 104 130 | 
             
                    # Unless we can resolve a new version, don't try to update to a latest
         | 
| 105 131 | 
             
                    # version (even for a library) as we rely on a resolvable version being
         | 
| @@ -109,29 +135,37 @@ module Dependabot | |
| 109 135 | 
             
                    library? ? latest_version&.to_s : preferred_resolvable_version.to_s
         | 
| 110 136 | 
             
                  end
         | 
| 111 137 |  | 
| 138 | 
            +
                  sig { returns(T::Boolean) }
         | 
| 112 139 | 
             
                  def library?
         | 
| 113 140 | 
             
                    # If it has a lockfile, treat it as an application. Otherwise treat it
         | 
| 114 141 | 
             
                    # as a library.
         | 
| 115 142 | 
             
                    dependency_files.none? { |f| f.name == "Cargo.lock" }
         | 
| 116 143 | 
             
                  end
         | 
| 117 144 |  | 
| 145 | 
            +
                  sig { returns(LatestVersionFinder) }
         | 
| 118 146 | 
             
                  def latest_version_finder
         | 
| 119 | 
            -
                    @latest_version_finder  | 
| 120 | 
            -
                       | 
| 121 | 
            -
                         | 
| 122 | 
            -
             | 
| 123 | 
            -
             | 
| 124 | 
            -
             | 
| 125 | 
            -
             | 
| 126 | 
            -
             | 
| 127 | 
            -
             | 
| 128 | 
            -
             | 
| 147 | 
            +
                    @latest_version_finder = T.let(
         | 
| 148 | 
            +
                      @latest_version_finder ||
         | 
| 149 | 
            +
                        LatestVersionFinder.new(
         | 
| 150 | 
            +
                          dependency: dependency,
         | 
| 151 | 
            +
                          dependency_files: dependency_files,
         | 
| 152 | 
            +
                          credentials: credentials,
         | 
| 153 | 
            +
                          ignored_versions: ignored_versions,
         | 
| 154 | 
            +
                          security_advisories: security_advisories,
         | 
| 155 | 
            +
                          cooldown_options: update_cooldown,
         | 
| 156 | 
            +
                          raise_on_ignored: raise_on_ignored
         | 
| 157 | 
            +
                        ),
         | 
| 158 | 
            +
                      T.nilable(LatestVersionFinder)
         | 
| 159 | 
            +
                    )
         | 
| 160 | 
            +
                    T.must(@latest_version_finder)
         | 
| 129 161 | 
             
                  end
         | 
| 130 162 |  | 
| 163 | 
            +
                  sig { returns(T.nilable(T.any(String, Gem::Version))) }
         | 
| 131 164 | 
             
                  def latest_version_for_git_dependency
         | 
| 132 165 | 
             
                    latest_git_version_sha
         | 
| 133 166 | 
             
                  end
         | 
| 134 167 |  | 
| 168 | 
            +
                  sig { returns(T.nilable(T.any(String, Gem::Version))) }
         | 
| 135 169 | 
             
                  def latest_git_version_sha
         | 
| 136 170 | 
             
                    # If the gem isn't pinned, the latest version is just the latest
         | 
| 137 171 | 
             
                    # commit for the specified branch.
         | 
| @@ -150,6 +184,7 @@ module Dependabot | |
| 150 184 | 
             
                    dependency.version
         | 
| 151 185 | 
             
                  end
         | 
| 152 186 |  | 
| 187 | 
            +
                  sig { returns(T.nilable(T.any(String, Gem::Version))) }
         | 
| 153 188 | 
             
                  def latest_resolvable_version_for_git_dependency
         | 
| 154 189 | 
             
                    # If the gem isn't pinned, the latest version is just the latest
         | 
| 155 190 | 
             
                    # commit for the specified branch.
         | 
| @@ -161,21 +196,28 @@ module Dependabot | |
| 161 196 | 
             
                    if git_commit_checker.pinned_ref_looks_like_version? &&
         | 
| 162 197 | 
             
                       latest_git_tag_is_resolvable?
         | 
| 163 198 | 
             
                      new_tag = git_commit_checker.local_tag_for_latest_version
         | 
| 164 | 
            -
                      return new_tag.fetch(:commit_sha)
         | 
| 199 | 
            +
                      return T.must(new_tag).fetch(:commit_sha)
         | 
| 165 200 | 
             
                    end
         | 
| 166 201 |  | 
| 167 202 | 
             
                    # If the dependency is pinned then there's nothing we can do.
         | 
| 168 203 | 
             
                    dependency.version
         | 
| 169 204 | 
             
                  end
         | 
| 170 205 |  | 
| 206 | 
            +
                  sig { returns(T::Boolean) }
         | 
| 171 207 | 
             
                  def latest_git_tag_is_resolvable?
         | 
| 172 | 
            -
                     | 
| 208 | 
            +
                    unless defined?(@latest_git_tag_is_resolvable_checked)
         | 
| 209 | 
            +
                      @latest_git_tag_is_resolvable_checked = T.let(nil,
         | 
| 210 | 
            +
                                                                    T.nilable(T::Boolean))
         | 
| 211 | 
            +
                    end
         | 
| 212 | 
            +
                    @git_tag_resolvable = T.let(nil, T.nilable(T::Boolean)) unless defined?(@git_tag_resolvable)
         | 
| 213 | 
            +
             | 
| 214 | 
            +
                    return T.must(@git_tag_resolvable) if @latest_git_tag_is_resolvable_checked
         | 
| 173 215 |  | 
| 174 216 | 
             
                    @latest_git_tag_is_resolvable_checked = true
         | 
| 175 217 |  | 
| 176 218 | 
             
                    return false if git_commit_checker.local_tag_for_latest_version.nil?
         | 
| 177 219 |  | 
| 178 | 
            -
                    replacement_tag = git_commit_checker.local_tag_for_latest_version
         | 
| 220 | 
            +
                    replacement_tag = T.must(git_commit_checker.local_tag_for_latest_version)
         | 
| 179 221 |  | 
| 180 222 | 
             
                    prepared_files = FilePreparer.new(
         | 
| 181 223 | 
             
                      dependency_files: dependency_files,
         | 
| @@ -197,6 +239,7 @@ module Dependabot | |
| 197 239 | 
             
                    @git_tag_resolvable = false
         | 
| 198 240 | 
             
                  end
         | 
| 199 241 |  | 
| 242 | 
            +
                  sig { returns(T.nilable(T.any(String, Gem::Version))) }
         | 
| 200 243 | 
             
                  def latest_resolvable_commit_with_unchanged_git_source
         | 
| 201 244 | 
             
                    fetch_latest_resolvable_version(unlock_requirement: false)
         | 
| 202 245 | 
             
                  rescue SharedHelpers::HelperSubprocessFailed => e
         | 
| @@ -207,6 +250,7 @@ module Dependabot | |
| 207 250 | 
             
                    raise e
         | 
| 208 251 | 
             
                  end
         | 
| 209 252 |  | 
| 253 | 
            +
                  sig { params(unlock_requirement: T::Boolean).returns(T.nilable(T.any(String, Gem::Version))) }
         | 
| 210 254 | 
             
                  def fetch_latest_resolvable_version(unlock_requirement:)
         | 
| 211 255 | 
             
                    prepared_files = FilePreparer.new(
         | 
| 212 256 | 
             
                      dependency_files: dependency_files,
         | 
| @@ -223,6 +267,7 @@ module Dependabot | |
| 223 267 | 
             
                    ).latest_resolvable_version
         | 
| 224 268 | 
             
                  end
         | 
| 225 269 |  | 
| 270 | 
            +
                  sig { returns(T.nilable(T.any(String, Gem::Version))) }
         | 
| 226 271 | 
             
                  def fetch_lowest_resolvable_security_fix_version
         | 
| 227 272 | 
             
                    fix_version = lowest_security_fix_version
         | 
| 228 273 | 
             
                    return latest_resolvable_version if fix_version.nil?
         | 
| @@ -248,6 +293,7 @@ module Dependabot | |
| 248 293 | 
             
                    latest_resolvable_version
         | 
| 249 294 | 
             
                  end
         | 
| 250 295 |  | 
| 296 | 
            +
                  sig { returns(T.nilable(T::Hash[T.any(String, Symbol), T.untyped])) }
         | 
| 251 297 | 
             
                  def updated_source
         | 
| 252 298 | 
             
                    # Never need to update source, unless a git_dependency
         | 
| 253 299 | 
             
                    return dependency_source_details unless git_dependency?
         | 
| @@ -255,38 +301,47 @@ module Dependabot | |
| 255 301 | 
             
                    # Update the git tag if updating a pinned version
         | 
| 256 302 | 
             
                    if git_commit_checker.pinned_ref_looks_like_version? &&
         | 
| 257 303 | 
             
                       latest_git_tag_is_resolvable?
         | 
| 258 | 
            -
                      new_tag = git_commit_checker.local_tag_for_latest_version
         | 
| 259 | 
            -
                      return dependency_source_details.merge(ref: new_tag.fetch(:tag))
         | 
| 304 | 
            +
                      new_tag = T.must(git_commit_checker.local_tag_for_latest_version)
         | 
| 305 | 
            +
                      return T.must(dependency_source_details).merge(ref: new_tag.fetch(:tag))
         | 
| 260 306 | 
             
                    end
         | 
| 261 307 |  | 
| 262 308 | 
             
                    # Otherwise return the original source
         | 
| 263 309 | 
             
                    dependency_source_details
         | 
| 264 310 | 
             
                  end
         | 
| 265 311 |  | 
| 312 | 
            +
                  sig { returns(T.nilable(T::Hash[T.any(String, Symbol), T.untyped])) }
         | 
| 266 313 | 
             
                  def dependency_source_details
         | 
| 267 314 | 
             
                    dependency.source_details
         | 
| 268 315 | 
             
                  end
         | 
| 269 316 |  | 
| 317 | 
            +
                  sig { returns(T::Boolean) }
         | 
| 270 318 | 
             
                  def git_dependency?
         | 
| 271 319 | 
             
                    git_commit_checker.git_dependency?
         | 
| 272 320 | 
             
                  end
         | 
| 273 321 |  | 
| 322 | 
            +
                  sig { returns(T::Boolean) }
         | 
| 274 323 | 
             
                  def git_subdependency?
         | 
| 275 324 | 
             
                    return false if dependency.top_level?
         | 
| 276 325 |  | 
| 277 326 | 
             
                    !version_class.correct?(dependency.version)
         | 
| 278 327 | 
             
                  end
         | 
| 279 328 |  | 
| 329 | 
            +
                  sig { returns(T::Boolean) }
         | 
| 280 330 | 
             
                  def path_dependency?
         | 
| 281 331 | 
             
                    dependency.source_type == "path"
         | 
| 282 332 | 
             
                  end
         | 
| 283 333 |  | 
| 334 | 
            +
                  sig { returns(GitCommitChecker) }
         | 
| 284 335 | 
             
                  def git_commit_checker
         | 
| 285 | 
            -
                    @git_commit_checker  | 
| 286 | 
            -
                       | 
| 287 | 
            -
                         | 
| 288 | 
            -
             | 
| 289 | 
            -
             | 
| 336 | 
            +
                    @git_commit_checker = T.let(
         | 
| 337 | 
            +
                      @git_commit_checker ||
         | 
| 338 | 
            +
                        GitCommitChecker.new(
         | 
| 339 | 
            +
                          dependency: dependency,
         | 
| 340 | 
            +
                          credentials: credentials
         | 
| 341 | 
            +
                        ),
         | 
| 342 | 
            +
                      T.nilable(GitCommitChecker)
         | 
| 343 | 
            +
                    )
         | 
| 344 | 
            +
                    T.must(@git_commit_checker)
         | 
| 290 345 | 
             
                  end
         | 
| 291 346 | 
             
                end
         | 
| 292 347 | 
             
              end
         | 
| @@ -1,6 +1,7 @@ | |
| 1 | 
            -
            # typed:  | 
| 1 | 
            +
            # typed: strong
         | 
| 2 2 | 
             
            # frozen_string_literal: true
         | 
| 3 3 |  | 
| 4 | 
            +
            require "sorbet-runtime"
         | 
| 4 5 | 
             
            require "dependabot/version"
         | 
| 5 6 | 
             
            require "dependabot/utils"
         | 
| 6 7 |  | 
| @@ -11,26 +12,32 @@ require "dependabot/utils" | |
| 11 12 | 
             
            module Dependabot
         | 
| 12 13 | 
             
              module Cargo
         | 
| 13 14 | 
             
                class Version < Dependabot::Version
         | 
| 15 | 
            +
                  extend T::Sig
         | 
| 16 | 
            +
             | 
| 14 17 | 
             
                  VERSION_PATTERN = '[0-9]+(?>\.[0-9a-zA-Z]+)*' \
         | 
| 15 18 | 
             
                                    '(-[0-9A-Za-z-]+(\.[0-9a-zA-Z-]+)*)?' \
         | 
| 16 19 | 
             
                                    '(\+[0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*)?'
         | 
| 17 20 | 
             
                  ANCHORED_VERSION_PATTERN = /\A\s*(#{VERSION_PATTERN})?\s*\z/
         | 
| 18 21 |  | 
| 22 | 
            +
                  sig { override.params(version: VersionParameter).void }
         | 
| 19 23 | 
             
                  def initialize(version)
         | 
| 20 | 
            -
                    @version_string = version.to_s
         | 
| 24 | 
            +
                    @version_string = T.let(version.to_s, String)
         | 
| 21 25 | 
             
                    version = version.to_s.split("+").first if version.to_s.include?("+")
         | 
| 22 26 |  | 
| 23 27 | 
             
                    super
         | 
| 24 28 | 
             
                  end
         | 
| 25 29 |  | 
| 30 | 
            +
                  sig { override.returns(String) }
         | 
| 26 31 | 
             
                  def to_s
         | 
| 27 32 | 
             
                    @version_string
         | 
| 28 33 | 
             
                  end
         | 
| 29 34 |  | 
| 35 | 
            +
                  sig { override.returns(String) }
         | 
| 30 36 | 
             
                  def inspect # :nodoc:
         | 
| 31 37 | 
             
                    "#<#{self.class} #{@version_string}>"
         | 
| 32 38 | 
             
                  end
         | 
| 33 39 |  | 
| 40 | 
            +
                  sig { override.params(version: VersionParameter).returns(T::Boolean) }
         | 
| 34 41 | 
             
                  def self.correct?(version)
         | 
| 35 42 | 
             
                    return false if version.nil?
         | 
| 36 43 |  | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: dependabot-cargo
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.333.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Dependabot
         | 
| @@ -15,14 +15,14 @@ dependencies: | |
| 15 15 | 
             
                requirements:
         | 
| 16 16 | 
             
                - - '='
         | 
| 17 17 | 
             
                  - !ruby/object:Gem::Version
         | 
| 18 | 
            -
                    version: 0. | 
| 18 | 
            +
                    version: 0.333.0
         | 
| 19 19 | 
             
              type: :runtime
         | 
| 20 20 | 
             
              prerelease: false
         | 
| 21 21 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 22 22 | 
             
                requirements:
         | 
| 23 23 | 
             
                - - '='
         | 
| 24 24 | 
             
                  - !ruby/object:Gem::Version
         | 
| 25 | 
            -
                    version: 0. | 
| 25 | 
            +
                    version: 0.333.0
         | 
| 26 26 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 27 27 | 
             
              name: debug
         | 
| 28 28 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -211,14 +211,14 @@ dependencies: | |
| 211 211 | 
             
                requirements:
         | 
| 212 212 | 
             
                - - "~>"
         | 
| 213 213 | 
             
                  - !ruby/object:Gem::Version
         | 
| 214 | 
            -
                    version: '3. | 
| 214 | 
            +
                    version: '3.25'
         | 
| 215 215 | 
             
              type: :development
         | 
| 216 216 | 
             
              prerelease: false
         | 
| 217 217 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 218 218 | 
             
                requirements:
         | 
| 219 219 | 
             
                - - "~>"
         | 
| 220 220 | 
             
                  - !ruby/object:Gem::Version
         | 
| 221 | 
            -
                    version: '3. | 
| 221 | 
            +
                    version: '3.25'
         | 
| 222 222 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 223 223 | 
             
              name: webrick
         | 
| 224 224 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -266,7 +266,7 @@ licenses: | |
| 266 266 | 
             
            - MIT
         | 
| 267 267 | 
             
            metadata:
         | 
| 268 268 | 
             
              bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
         | 
| 269 | 
            -
              changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0. | 
| 269 | 
            +
              changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.333.0
         | 
| 270 270 | 
             
            rdoc_options: []
         | 
| 271 271 | 
             
            require_paths:
         | 
| 272 272 | 
             
            - lib
         |