Sam Ford 64a7c1f821
Git: Remove tags_only_debian logic
The `tags_only_debian` code in livecheck's `Git` strategy was
originally introduced in Homebrew/homebrew-livecheck#131 when
livecheck was in a less mature state and relied more on internal
special-casing like this (i.e., while we worked to add appropriate
`livecheck` blocks). This logic only has the potential to be
beneficial when a formula/cask doesn't contain a `livecheck` block
but I would argue that we shouldn't be making assumptions in the
strategy around whether tags with a `debian/` prefix should be
matched or omitted. The answer depends upon the context of a given
formula/cask and should be handled with a `livecheck` block, as we
do with other situations like this.
2022-06-07 09:02:03 -04:00

154 lines
5.6 KiB
Ruby

# typed: false
# frozen_string_literal: true
require "open3"
module Homebrew
module Livecheck
module Strategy
# The {Git} strategy identifies versions of software in a Git repository
# by checking the tags using `git ls-remote --tags`.
#
# Livecheck has historically prioritized the {Git} strategy over others
# and this behavior was continued when the priority setup was created.
# This is partly related to Livecheck checking formula URLs in order of
# `head`, `stable`, and then `homepage`. The higher priority here may
# be removed (or altered) in the future if we reevaluate this particular
# behavior.
#
# This strategy does not have a default regex. Instead, it simply removes
# any non-digit text from the start of tags and parses the rest as a
# {Version}. This works for some simple situations but even one unusual
# tag can cause a bad result. It's better to provide a regex in a
# `livecheck` block, so `livecheck` only matches what we really want.
#
# @api public
class Git
extend T::Sig
# The priority of the strategy on an informal scale of 1 to 10 (from
# lowest to highest).
PRIORITY = 8
# The default regex used to naively identify versions from tags when a
# regex isn't provided.
DEFAULT_REGEX = /\D*(.+)/.freeze
# Whether the strategy can be applied to the provided URL.
#
# @param url [String] the URL to match against
# @return [Boolean]
sig { params(url: String).returns(T::Boolean) }
def self.match?(url)
(DownloadStrategyDetector.detect(url) <= GitDownloadStrategy) == true
end
# Fetches a remote Git repository's tags using `git ls-remote --tags`
# and parses the command's output. If a regex is provided, it will be
# used to filter out any tags that don't match it.
#
# @param url [String] the URL of the Git repository to check
# @param regex [Regexp] the regex to use for filtering tags
# @return [Hash]
sig { params(url: String, regex: T.nilable(Regexp)).returns(T::Hash[Symbol, T.untyped]) }
def self.tag_info(url, regex = nil)
# Open3#capture3 is used here because we need to capture stderr
# output and handle it in an appropriate manner. Alternatives like
# SystemCommand always print errors (as well as debug output) and
# don't meet the same goals.
stdout_str, stderr_str, _status = Open3.capture3(
{ "GIT_TERMINAL_PROMPT" => "0" }, "git", "ls-remote", "--tags", url
)
tags_data = { tags: [] }
tags_data[:messages] = stderr_str.split("\n") if stderr_str.present?
return tags_data if stdout_str.blank?
# Isolate tag strings by removing leading/trailing text
stdout_str.gsub!(%r{^.*\trefs/tags/}, "")
stdout_str.gsub!("^{}", "")
tags = stdout_str.split("\n").uniq.sort
tags.select! { |t| t =~ regex } if regex
tags_data[:tags] = tags
tags_data
end
# Identify versions from tag strings using a provided regex or the
# `DEFAULT_REGEX`. The regex is expected to use a capture group around
# the version text.
#
# @param tags [Array] the tags to identify versions from
# @param regex [Regexp, nil] a regex to identify versions
# @return [Array]
sig {
params(
tags: T::Array[String],
regex: T.nilable(Regexp),
block: T.untyped,
).returns(T::Array[String])
}
def self.versions_from_tags(tags, regex = nil, &block)
if block
block_return_value = if regex.present?
yield(tags, regex)
elsif block.arity == 2
yield(tags, DEFAULT_REGEX)
else
yield(tags)
end
return Strategy.handle_block_return(block_return_value)
end
tags.map do |tag|
if regex
# Use the first capture group (the version)
tag.scan(regex).first&.first
else
# Remove non-digits from the start of the tag and use that as the
# version text
tag[DEFAULT_REGEX, 1]
end
end.compact.uniq
end
# Checks the Git tags for new versions. When a regex isn't provided,
# this strategy simply removes non-digits from the start of tag
# strings and parses the remaining text as a {Version}.
#
# @param url [String] the URL of the Git repository to check
# @param regex [Regexp, nil] a regex used for matching versions
# @return [Hash]
sig {
params(
url: String,
regex: T.nilable(Regexp),
_unused: T.nilable(T::Hash[Symbol, T.untyped]),
block: T.untyped,
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url:, regex: nil, **_unused, &block)
match_data = { matches: {}, regex: regex, url: url }
tags_data = tag_info(url, regex)
tags = tags_data[:tags]
if tags_data.key?(:messages)
match_data[:messages] = tags_data[:messages]
return match_data if tags.blank?
end
versions_from_tags(tags, regex, &block).each do |version_text|
match_data[:matches][version_text] = Version.new(version_text)
rescue TypeError
next
end
match_data
end
end
end
end
end