dependabot-docker_compose 0.297.1
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 +7 -0
- data/lib/dependabot/docker_compose/file_fetcher.rb +67 -0
- data/lib/dependabot/docker_compose/file_parser.rb +76 -0
- data/lib/dependabot/docker_compose/file_updater.rb +106 -0
- data/lib/dependabot/docker_compose/metadata_finder.rb +39 -0
- data/lib/dependabot/docker_compose/package_manager.rb +53 -0
- data/lib/dependabot/docker_compose/requirement.rb +43 -0
- data/lib/dependabot/docker_compose/tag.rb +144 -0
- data/lib/dependabot/docker_compose/update_checker.rb +479 -0
- data/lib/dependabot/docker_compose/version.rb +84 -0
- data/lib/dependabot/docker_compose.rb +19 -0
- data/lib/dependabot/shared/shared_file_fetcher.rb +99 -0
- data/lib/dependabot/shared/shared_file_parser.rb +80 -0
- data/lib/dependabot/shared/shared_file_updater.rb +261 -0
- data/lib/dependabot/shared/utils/credentials_finder.rb +101 -0
- data/lib/dependabot/shared/utils/helpers.rb +19 -0
- metadata +285 -0
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            SHA256:
         | 
| 3 | 
            +
              metadata.gz: e8800055af3f3a9fc1a7b056288ea510770fd65dcf8933eb69a66a4aed591914
         | 
| 4 | 
            +
              data.tar.gz: 05c25842d1a91b7fc0cddff8e51b0687d5ff0f1cb200276409e25671cf245f46
         | 
| 5 | 
            +
            SHA512:
         | 
| 6 | 
            +
              metadata.gz: c8f586a6ee7a26cb2e0e92abca5e23a0304537236df1fac702a667dfb8117494ce16d3d3243e20b4003e181c129e998381c7e5cca72da98ffaab7f2ff5aaf9b1
         | 
| 7 | 
            +
              data.tar.gz: 72d33eae6923a14d9ffc8f12ab9cfdf0cddab97ce91afa51a39be2bf8fa618bbb678cdc6408166f54745d3c5305800be91d5328c6c2a9eef8d421cfc7664499e
         | 
| @@ -0,0 +1,67 @@ | |
| 1 | 
            +
            # typed: strict
         | 
| 2 | 
            +
            # frozen_string_literal: true
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            require "dependabot/shared/shared_file_fetcher"
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module Dependabot
         | 
| 7 | 
            +
              module DockerCompose
         | 
| 8 | 
            +
                class FileFetcher < Dependabot::Shared::SharedFileFetcher
         | 
| 9 | 
            +
                  FILENAME_REGEX = /(docker-)?compose(-[\w]+)?(?>\.[\w-]+)?\.ya?ml/i
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  sig { override.returns(T::Array[DependencyFile]) }
         | 
| 12 | 
            +
                  def fetch_files
         | 
| 13 | 
            +
                    fetched_files = []
         | 
| 14 | 
            +
                    fetched_files += correctly_encoded_docker_compose_files if allow_beta_ecosystems?
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    return fetched_files if fetched_files.any?
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                    raise_appropriate_error
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  sig { override.returns(Regexp) }
         | 
| 22 | 
            +
                  def self.filename_regex
         | 
| 23 | 
            +
                    FILENAME_REGEX
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  sig { returns(T::Array[Dependabot::DependencyFile]) }
         | 
| 27 | 
            +
                  def docker_compose_files
         | 
| 28 | 
            +
                    @docker_compose_files ||=
         | 
| 29 | 
            +
                      T.let(repo_contents(raise_errors: false)
         | 
| 30 | 
            +
                      .select { |f| f.type == "file" && f.name.match?(FILENAME_REGEX) }
         | 
| 31 | 
            +
                      .map { |f| fetch_file_from_host(f.name) }, T.nilable(T::Array[DependencyFile]))
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  sig { returns(T::Array[Dependabot::DependencyFile]) }
         | 
| 35 | 
            +
                  def correctly_encoded_docker_compose_files
         | 
| 36 | 
            +
                    docker_compose_files.select { |f| T.must(f.content).valid_encoding? }
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                  sig { returns(T::Array[Dependabot::DependencyFile]) }
         | 
| 40 | 
            +
                  def incorrectly_encoded_docker_compose_files
         | 
| 41 | 
            +
                    docker_compose_files.reject { |f| T.must(f.content).valid_encoding? }
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  sig { override.returns(String) }
         | 
| 45 | 
            +
                  def self.required_files_message
         | 
| 46 | 
            +
                    "Repo must contain a docker-compose.yaml file."
         | 
| 47 | 
            +
                  end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                  private
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                  sig { override.returns(String) }
         | 
| 52 | 
            +
                  def default_file_name
         | 
| 53 | 
            +
                    "docker-compose.yml"
         | 
| 54 | 
            +
                  end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                  sig { override.returns(String) }
         | 
| 57 | 
            +
                  def file_type
         | 
| 58 | 
            +
                    "Docker Compose"
         | 
| 59 | 
            +
                  end
         | 
| 60 | 
            +
                end
         | 
| 61 | 
            +
              end
         | 
| 62 | 
            +
            end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
            Dependabot::FileFetchers.register(
         | 
| 65 | 
            +
              "docker_compose",
         | 
| 66 | 
            +
              Dependabot::DockerCompose::FileFetcher
         | 
| 67 | 
            +
            )
         | 
| @@ -0,0 +1,76 @@ | |
| 1 | 
            +
            # typed: strict
         | 
| 2 | 
            +
            # frozen_string_literal: true
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            require "yaml"
         | 
| 5 | 
            +
            require "dependabot/shared/shared_file_parser"
         | 
| 6 | 
            +
            require "dependabot/docker_compose/package_manager"
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            module Dependabot
         | 
| 9 | 
            +
              module DockerCompose
         | 
| 10 | 
            +
                class FileParser < Dependabot::Shared::SharedFileParser
         | 
| 11 | 
            +
                  extend T::Sig
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  FROM_IMAGE = %r{^(?:#{REGISTRY}/)?#{IMAGE}(?:#{TAG})?(?:#{DIGEST})?(?:#{NAME})?}
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  sig { returns(Ecosystem) }
         | 
| 16 | 
            +
                  def ecosystem
         | 
| 17 | 
            +
                    @ecosystem ||= T.let(
         | 
| 18 | 
            +
                      Ecosystem.new(
         | 
| 19 | 
            +
                        name: ECOSYSTEM,
         | 
| 20 | 
            +
                        package_manager: DockerPackageManager.new
         | 
| 21 | 
            +
                      ),
         | 
| 22 | 
            +
                      T.nilable(Ecosystem)
         | 
| 23 | 
            +
                    )
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  sig { override.returns(T::Array[Dependabot::Dependency]) }
         | 
| 27 | 
            +
                  def parse
         | 
| 28 | 
            +
                    dependency_set = DependencySet.new
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                    composefiles.each do |composefile|
         | 
| 31 | 
            +
                      yaml = YAML.safe_load(T.must(composefile.content))
         | 
| 32 | 
            +
                      yaml["services"].each do |_, service|
         | 
| 33 | 
            +
                        parsed_from_image = T.must(FROM_IMAGE.match(service["image"])).named_captures
         | 
| 34 | 
            +
                        parsed_from_image["registry"] = nil if parsed_from_image["registry"] == "docker.io"
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                        version = version_from(parsed_from_image)
         | 
| 37 | 
            +
                        next unless version
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                        dependency_set << build_dependency(composefile, parsed_from_image, version)
         | 
| 40 | 
            +
                      end
         | 
| 41 | 
            +
                    end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                    dependency_set.dependencies
         | 
| 44 | 
            +
                  end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                  private
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  sig { override.returns(String) }
         | 
| 49 | 
            +
                  def package_manager
         | 
| 50 | 
            +
                    "docker_compose"
         | 
| 51 | 
            +
                  end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                  sig { override.returns(String) }
         | 
| 54 | 
            +
                  def file_type
         | 
| 55 | 
            +
                    "docker-compose.yml"
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                  sig { returns(T::Array[Dependabot::DependencyFile]) }
         | 
| 59 | 
            +
                  def composefiles
         | 
| 60 | 
            +
                    dependency_files
         | 
| 61 | 
            +
                  end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                  sig { override.void }
         | 
| 64 | 
            +
                  def check_required_files
         | 
| 65 | 
            +
                    return if dependency_files.any?
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                    raise "No #{file_type}!"
         | 
| 68 | 
            +
                  end
         | 
| 69 | 
            +
                end
         | 
| 70 | 
            +
              end
         | 
| 71 | 
            +
            end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
            Dependabot::FileParsers.register(
         | 
| 74 | 
            +
              "docker_compose",
         | 
| 75 | 
            +
              Dependabot::DockerCompose::FileParser
         | 
| 76 | 
            +
            )
         | 
| @@ -0,0 +1,106 @@ | |
| 1 | 
            +
            # typed: strict
         | 
| 2 | 
            +
            # frozen_string_literal: true
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            require "dependabot/shared/shared_file_updater"
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module Dependabot
         | 
| 7 | 
            +
              module DockerCompose
         | 
| 8 | 
            +
                class FileUpdater < Dependabot::Shared::SharedFileUpdater
         | 
| 9 | 
            +
                  extend T::Sig
         | 
| 10 | 
            +
                  extend T::Helpers
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  YAML_REGEXP = /(docker-)?compose(?>\.[\w-]+)?\.ya?ml/i
         | 
| 13 | 
            +
                  IMAGE_REGEX = /image:\s*/
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  sig { override.returns(T::Array[Regexp]) }
         | 
| 16 | 
            +
                  def self.updated_files_regex
         | 
| 17 | 
            +
                    [YAML_REGEXP]
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  sig { override.returns(String) }
         | 
| 21 | 
            +
                  def file_type
         | 
| 22 | 
            +
                    "Docker compose"
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  sig { override.returns(Regexp) }
         | 
| 26 | 
            +
                  def yaml_file_pattern
         | 
| 27 | 
            +
                    YAML_REGEXP
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  sig { override.returns(Regexp) }
         | 
| 31 | 
            +
                  def container_image_regex
         | 
| 32 | 
            +
                    IMAGE_REGEX
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  sig { override.params(escaped_declaration: String).returns(Regexp) }
         | 
| 36 | 
            +
                  def build_old_declaration_regex(escaped_declaration)
         | 
| 37 | 
            +
                    %r{#{IMAGE_REGEX}\s+(docker\.io/)?#{escaped_declaration}(?=\s|$)}
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  sig { override.returns(T::Array[Dependabot::DependencyFile]) }
         | 
| 41 | 
            +
                  def updated_dependency_files
         | 
| 42 | 
            +
                    updated_files = []
         | 
| 43 | 
            +
                    dependency_files.each do |file|
         | 
| 44 | 
            +
                      next unless requirement_changed?(file, T.must(dependency))
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                      updated_files << updated_file(
         | 
| 47 | 
            +
                        file: file,
         | 
| 48 | 
            +
                        content: T.must(updated_dockerfile_content(file))
         | 
| 49 | 
            +
                      )
         | 
| 50 | 
            +
                    end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                    updated_files.reject! { |f| dependency_files.include?(f) }
         | 
| 53 | 
            +
                    raise "No files changed!" if updated_files.none?
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                    updated_files
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                  sig do
         | 
| 59 | 
            +
                    override.params(previous_content: String, old_source: T::Hash[Symbol, T.nilable(String)],
         | 
| 60 | 
            +
                                    new_source: T::Hash[Symbol, T.nilable(String)]).returns(String)
         | 
| 61 | 
            +
                  end
         | 
| 62 | 
            +
                  def update_digest_and_tag(previous_content, old_source, new_source)
         | 
| 63 | 
            +
                    old_digest = old_source[:digest]
         | 
| 64 | 
            +
                    new_digest = new_source[:digest]
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                    old_tag = old_source[:tag]
         | 
| 67 | 
            +
                    new_tag = new_source[:tag]
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                    old_declaration =
         | 
| 70 | 
            +
                      if private_registry_url(old_source)
         | 
| 71 | 
            +
                        "#{private_registry_url(old_source)}/"
         | 
| 72 | 
            +
                      else
         | 
| 73 | 
            +
                        ""
         | 
| 74 | 
            +
                      end
         | 
| 75 | 
            +
                    old_declaration += T.must(dependency).name
         | 
| 76 | 
            +
                    old_declaration +=
         | 
| 77 | 
            +
                      if specified_with_tag?(old_source)
         | 
| 78 | 
            +
                        ":#{old_tag}"
         | 
| 79 | 
            +
                      else
         | 
| 80 | 
            +
                        ""
         | 
| 81 | 
            +
                      end
         | 
| 82 | 
            +
                    old_declaration +=
         | 
| 83 | 
            +
                      if old_digest&.start_with?("sha256:")
         | 
| 84 | 
            +
                        "@#{old_digest}"
         | 
| 85 | 
            +
                      else
         | 
| 86 | 
            +
                        ""
         | 
| 87 | 
            +
                      end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                    escaped_declaration = Regexp.escape(old_declaration)
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                    old_declaration_regex = build_old_declaration_regex(escaped_declaration)
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                    previous_content.gsub(old_declaration_regex) do |old_dec|
         | 
| 94 | 
            +
                      old_dec
         | 
| 95 | 
            +
                        .gsub(":#{old_tag}", ":#{new_tag}")
         | 
| 96 | 
            +
                        .gsub(old_digest.to_s, new_digest.to_s)
         | 
| 97 | 
            +
                    end
         | 
| 98 | 
            +
                  end
         | 
| 99 | 
            +
                end
         | 
| 100 | 
            +
              end
         | 
| 101 | 
            +
            end
         | 
| 102 | 
            +
             | 
| 103 | 
            +
            Dependabot::FileUpdaters.register(
         | 
| 104 | 
            +
              "docker_compose",
         | 
| 105 | 
            +
              Dependabot::DockerCompose::FileUpdater
         | 
| 106 | 
            +
            )
         | 
| @@ -0,0 +1,39 @@ | |
| 1 | 
            +
            # typed: strict
         | 
| 2 | 
            +
            # frozen_string_literal: true
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            require "dependabot/metadata_finders"
         | 
| 5 | 
            +
            require "dependabot/metadata_finders/base"
         | 
| 6 | 
            +
            require "dependabot/shared_helpers"
         | 
| 7 | 
            +
            require "sorbet-runtime"
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            module Dependabot
         | 
| 10 | 
            +
              module DockerCompose
         | 
| 11 | 
            +
                class MetadataFinder < Dependabot::MetadataFinders::Base
         | 
| 12 | 
            +
                  extend T::Sig
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  private
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  sig { override.returns(T.nilable(Dependabot::Source)) }
         | 
| 17 | 
            +
                  def look_up_source
         | 
| 18 | 
            +
                    return if dependency.requirements.empty?
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                    new_source = dependency.requirements.first&.fetch(:source)
         | 
| 21 | 
            +
                    return unless new_source && new_source[:registry] && new_source[:tag]
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                    image_ref = "#{new_source[:registry]}/#{dependency.name}:#{new_source[:tag]}"
         | 
| 24 | 
            +
                    image_details_output = SharedHelpers.run_shell_command("regctl image inspect #{image_ref}")
         | 
| 25 | 
            +
                    image_details = JSON.parse(image_details_output)
         | 
| 26 | 
            +
                    image_source = image_details.dig("config", "Labels", "org.opencontainers.image.source")
         | 
| 27 | 
            +
                    return unless image_source
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                    Dependabot::Source.from_url(image_source)
         | 
| 30 | 
            +
                  rescue StandardError => e
         | 
| 31 | 
            +
                    Dependabot.logger.warn("Error looking up Docker Compose source: #{e.message}")
         | 
| 32 | 
            +
                    nil
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
              end
         | 
| 36 | 
            +
            end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
            Dependabot::MetadataFinders
         | 
| 39 | 
            +
              .register("docker_compose", Dependabot::DockerCompose::MetadataFinder)
         | 
| @@ -0,0 +1,53 @@ | |
| 1 | 
            +
            # typed: strong
         | 
| 2 | 
            +
            # frozen_string_literal: true
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            require "sorbet-runtime"
         | 
| 5 | 
            +
            require "dependabot/docker_compose/version"
         | 
| 6 | 
            +
            require "dependabot/ecosystem"
         | 
| 7 | 
            +
            require "dependabot/docker_compose/requirement"
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            module Dependabot
         | 
| 10 | 
            +
              module DockerCompose
         | 
| 11 | 
            +
                ECOSYSTEM = "docker_compose"
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                SUPPORTED_VERSIONS = T.let([].freeze, T::Array[Dependabot::Version])
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                DEPRECATED_VERSIONS = T.let([].freeze, T::Array[Dependabot::Version])
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                class DockerPackageManager < Dependabot::Ecosystem::VersionManager
         | 
| 18 | 
            +
                  extend T::Sig
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  NAME = "docker_compose"
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  # As docker_compose updater is an in house custom utility, We use a placeholder
         | 
| 23 | 
            +
                  # version number for docker_compose updater
         | 
| 24 | 
            +
                  VERSION = "1.0.0"
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  SUPPORTED_VERSIONS = T.let([].freeze, T::Array[Dependabot::Version])
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  DEPRECATED_VERSIONS = T.let([].freeze, T::Array[Dependabot::Version])
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  sig do
         | 
| 31 | 
            +
                    void
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
                  def initialize
         | 
| 34 | 
            +
                    super(
         | 
| 35 | 
            +
                      name: NAME,
         | 
| 36 | 
            +
                      version: Version.new(VERSION),
         | 
| 37 | 
            +
                      deprecated_versions: DEPRECATED_VERSIONS,
         | 
| 38 | 
            +
                      supported_versions: SUPPORTED_VERSIONS
         | 
| 39 | 
            +
                   )
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  sig { override.returns(T::Boolean) }
         | 
| 43 | 
            +
                  def deprecated?
         | 
| 44 | 
            +
                    false
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                  sig { override.returns(T::Boolean) }
         | 
| 48 | 
            +
                  def unsupported?
         | 
| 49 | 
            +
                    false
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
              end
         | 
| 53 | 
            +
            end
         | 
| @@ -0,0 +1,43 @@ | |
| 1 | 
            +
            # typed: strict
         | 
| 2 | 
            +
            # frozen_string_literal: true
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            require "sorbet-runtime"
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            require "dependabot/requirement"
         | 
| 7 | 
            +
            require "dependabot/utils"
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            module Dependabot
         | 
| 10 | 
            +
              module DockerCompose
         | 
| 11 | 
            +
                # Lifted from the bundler package manager
         | 
| 12 | 
            +
                class Requirement < Dependabot::Requirement
         | 
| 13 | 
            +
                  extend T::Sig
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  # For consistency with other languages, we define a requirements array.
         | 
| 16 | 
            +
                  # Ruby doesn't have an `OR` separator for requirements, so it always
         | 
| 17 | 
            +
                  # contains a single element.
         | 
| 18 | 
            +
                  sig { override.params(requirement_string: T.nilable(String)).returns(T::Array[Requirement]) }
         | 
| 19 | 
            +
                  def self.requirements_array(requirement_string)
         | 
| 20 | 
            +
                    [new(T.must(requirement_string))]
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  sig { override.params(version: Version).returns(T::Boolean) }
         | 
| 24 | 
            +
                  def satisfied_by?(version)
         | 
| 25 | 
            +
                    super(version.release_part)
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  # Patches Gem::Requirement to make it accept requirement strings like
         | 
| 29 | 
            +
                  # "~> 4.2.5, >= 4.2.5.1" without first needing to split them.
         | 
| 30 | 
            +
                  sig { params(requirements: T.any(T.nilable(String), T::Array[T.nilable(String)])).void }
         | 
| 31 | 
            +
                  def initialize(*requirements)
         | 
| 32 | 
            +
                    requirements = requirements.flatten.flat_map do |req_string|
         | 
| 33 | 
            +
                      req_string.to_s.split(",").map(&:strip)
         | 
| 34 | 
            +
                    end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                    super(requirements)
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
              end
         | 
| 40 | 
            +
            end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
            Dependabot::Utils
         | 
| 43 | 
            +
              .register_requirement_class("docker_compose", Dependabot::DockerCompose::Requirement)
         | 
| @@ -0,0 +1,144 @@ | |
| 1 | 
            +
            # typed: strong
         | 
| 2 | 
            +
            # frozen_string_literal: true
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            require "sorbet-runtime"
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module Dependabot
         | 
| 7 | 
            +
              module DockerCompose
         | 
| 8 | 
            +
                class Tag
         | 
| 9 | 
            +
                  extend T::Sig
         | 
| 10 | 
            +
                  WORDS_WITH_BUILD = /(?:(?:-[a-z]+)+-[0-9]+)+/
         | 
| 11 | 
            +
                  VERSION_REGEX = /v?(?<version>[0-9]+(?:[_.][0-9]+)*(?:\.[a-z0-9]+|#{WORDS_WITH_BUILD}|-(?:kb)?[0-9]+)*)/i
         | 
| 12 | 
            +
                  VERSION_WITH_SFX = /^#{VERSION_REGEX}(?<suffix>-[a-z][a-z0-9.\-]*)?$/i
         | 
| 13 | 
            +
                  VERSION_WITH_PFX = /^(?<prefix>[a-z][a-z0-9.\-_]*-)?#{VERSION_REGEX}$/i
         | 
| 14 | 
            +
                  VERSION_WITH_PFX_AND_SFX = /^(?<prefix>[a-z\-_]+-)?#{VERSION_REGEX}(?<suffix>-[a-z\-]+)?$/i
         | 
| 15 | 
            +
                  NAME_WITH_VERSION =
         | 
| 16 | 
            +
                    /
         | 
| 17 | 
            +
                      #{VERSION_WITH_PFX}|
         | 
| 18 | 
            +
                      #{VERSION_WITH_SFX}|
         | 
| 19 | 
            +
                      #{VERSION_WITH_PFX_AND_SFX}
         | 
| 20 | 
            +
                  /x
         | 
| 21 | 
            +
                  DIGEST = /@(?<digest>[^\s]+)/
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  sig { returns(String) }
         | 
| 24 | 
            +
                  attr_reader :name
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  sig { params(name: String).void }
         | 
| 27 | 
            +
                  def initialize(name)
         | 
| 28 | 
            +
                    @name = name
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  sig { returns(String) }
         | 
| 32 | 
            +
                  def to_s
         | 
| 33 | 
            +
                    name
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  sig { returns(T::Boolean) }
         | 
| 37 | 
            +
                  def digest?
         | 
| 38 | 
            +
                    name.match?(DIGEST)
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                  sig { returns(T.nilable(T::Boolean)) }
         | 
| 42 | 
            +
                  def looks_like_prerelease?
         | 
| 43 | 
            +
                    numeric_version&.match?(/[a-zA-Z]/)
         | 
| 44 | 
            +
                  end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                  sig { params(other: Tag).returns(T::Boolean) }
         | 
| 47 | 
            +
                  def comparable_to?(other)
         | 
| 48 | 
            +
                    return false unless comparable?
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                    other_prefix = other.prefix
         | 
| 51 | 
            +
                    other_suffix = other.suffix
         | 
| 52 | 
            +
                    other_format = other.format
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                    equal_prefix = prefix == other_prefix
         | 
| 55 | 
            +
                    equal_format = format == other_format
         | 
| 56 | 
            +
                    return equal_prefix && equal_format if other_format == :sha_suffixed
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                    equal_suffix = suffix == other_suffix
         | 
| 59 | 
            +
                    equal_prefix && equal_format && equal_suffix
         | 
| 60 | 
            +
                  end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                  sig { returns(T::Boolean) }
         | 
| 63 | 
            +
                  def comparable?
         | 
| 64 | 
            +
                    name.match?(NAME_WITH_VERSION)
         | 
| 65 | 
            +
                  end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                  sig { params(other: Tag).returns(T::Boolean) }
         | 
| 68 | 
            +
                  def same_precision?(other)
         | 
| 69 | 
            +
                    other.precision == precision
         | 
| 70 | 
            +
                  end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                  sig { params(other: Tag).returns(T::Boolean) }
         | 
| 73 | 
            +
                  def same_but_less_precise?(other)
         | 
| 74 | 
            +
                    other.segments.zip(segments).all? do |segment, other_segment|
         | 
| 75 | 
            +
                      segment == other_segment || other_segment.nil?
         | 
| 76 | 
            +
                    end
         | 
| 77 | 
            +
                  end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                  sig { returns(T.nilable(T::Boolean)) }
         | 
| 80 | 
            +
                  def canonical?
         | 
| 81 | 
            +
                    return false unless numeric_version
         | 
| 82 | 
            +
                    return true if name == numeric_version
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                    # .NET tags are suffixed with -sdk
         | 
| 85 | 
            +
                    return true if numeric_version && name == numeric_version.to_s + "-sdk"
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                    numeric_version && name == "jdk-" + T.must(numeric_version)
         | 
| 88 | 
            +
                  end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                  sig { returns T.nilable(String) }
         | 
| 91 | 
            +
                  def prefix
         | 
| 92 | 
            +
                    name.match(NAME_WITH_VERSION)&.named_captures&.fetch("prefix")
         | 
| 93 | 
            +
                  end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                  sig { returns T.nilable(String) }
         | 
| 96 | 
            +
                  def suffix
         | 
| 97 | 
            +
                    name.match(NAME_WITH_VERSION)&.named_captures&.fetch("suffix")
         | 
| 98 | 
            +
                  end
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                  sig { returns T.nilable(String) }
         | 
| 101 | 
            +
                  def version
         | 
| 102 | 
            +
                    name.match(NAME_WITH_VERSION)&.named_captures&.fetch("version")
         | 
| 103 | 
            +
                  end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                  sig { returns(Symbol) }
         | 
| 106 | 
            +
                  def format
         | 
| 107 | 
            +
                    return :sha_suffixed if name.match?(/(^|\-g?)[0-9a-f]{7,}$/)
         | 
| 108 | 
            +
                    return :year_month if version&.match?(/^[12]\d{3}(?:[.\-]|$)/)
         | 
| 109 | 
            +
                    return :year_month_day if version&.match?(/^[12](?:\d{5}|\d{7})(?:[.\-]|$)/)
         | 
| 110 | 
            +
                    return :build_num if version&.match?(/^\d+$/)
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                    # As an example, "21-ea-32", "22-ea-7", and "22-ea-jdk-nanoserver-1809"
         | 
| 113 | 
            +
                    # are mapped to "<version>-ea-<build_num>", "<version>-ea-<build_num>",
         | 
| 114 | 
            +
                    # and "<version>-ea-jdk-nanoserver-<build_num>" respectively.
         | 
| 115 | 
            +
                    #
         | 
| 116 | 
            +
                    # That means only "22-ea-7" will be considered as a viable update
         | 
| 117 | 
            +
                    # candidate for "21-ea-32", since it's the only one that respects that
         | 
| 118 | 
            +
                    # format.
         | 
| 119 | 
            +
                    if version&.match?(WORDS_WITH_BUILD)
         | 
| 120 | 
            +
                      return :"<version>#{T.must(version).match(WORDS_WITH_BUILD).to_s.gsub(/-[0-9]+/, '-<build_num>')}"
         | 
| 121 | 
            +
                    end
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                    :normal
         | 
| 124 | 
            +
                  end
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                  sig { returns(T.nilable(String)) }
         | 
| 127 | 
            +
                  def numeric_version
         | 
| 128 | 
            +
                    return unless comparable?
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                    version&.gsub(/kb/i, "")&.gsub(/-[a-z]+/, "")&.downcase
         | 
| 131 | 
            +
                  end
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                  sig { returns(Integer) }
         | 
| 134 | 
            +
                  def precision
         | 
| 135 | 
            +
                    segments.length
         | 
| 136 | 
            +
                  end
         | 
| 137 | 
            +
             | 
| 138 | 
            +
                  sig { returns(T::Array[String]) }
         | 
| 139 | 
            +
                  def segments
         | 
| 140 | 
            +
                    T.must(numeric_version).split(/[.-]/)
         | 
| 141 | 
            +
                  end
         | 
| 142 | 
            +
                end
         | 
| 143 | 
            +
              end
         | 
| 144 | 
            +
            end
         |