Merge pull request #11779 from samford/livecheck/allow-nil-return-from-strategy-blocks

livecheck: allow nil return from strategy blocks
This commit is contained in:
Sam Ford 2021-08-03 08:57:46 -04:00 committed by GitHub
commit 1a55b749c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 125 additions and 60 deletions

View File

@ -52,7 +52,9 @@ module Homebrew
url: String,
regex: T.nilable(Regexp),
cask: T.nilable(Cask::Cask),
block: T.nilable(T.proc.params(arg0: String).returns(T.any(T::Array[String], String))),
block: T.nilable(
T.proc.params(arg0: String, arg1: Regexp).returns(T.any(String, T::Array[String], NilClass)),
),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask: nil, &block)

View File

@ -59,7 +59,9 @@ module Homebrew
url: String,
regex: T.nilable(Regexp),
cask: T.nilable(Cask::Cask),
block: T.nilable(T.proc.params(arg0: String).returns(T.any(T::Array[String], String))),
block: T.nilable(
T.proc.params(arg0: String, arg1: Regexp).returns(T.any(String, T::Array[String], NilClass)),
),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask: nil, &block)

View File

@ -50,7 +50,9 @@ module Homebrew
url: String,
regex: T.nilable(Regexp),
cask: T.nilable(Cask::Cask),
block: T.nilable(T.proc.params(arg0: String).returns(T.any(T::Array[String], String))),
block: T.nilable(
T.proc.params(arg0: String, arg1: Regexp).returns(T.any(String, T::Array[String], NilClass)),
),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask: nil, &block)

View File

@ -37,19 +37,24 @@ module Homebrew
sig {
params(
content: String,
block: T.nilable(T.proc.params(arg0: Hash).returns(String)),
block: T.nilable(T.proc.params(arg0: T::Hash[String, T.untyped]).returns(T.nilable(String))),
).returns(T.nilable(String))
}
def self.version_from_content(content, &block)
require "yaml"
return unless (yaml = YAML.safe_load(content))
yaml = YAML.safe_load(content)
return if yaml.blank?
if block
value = block.call(yaml)
return value if value.is_a?(String)
raise TypeError, "Return value of `strategy :electron_builder` block must be a string."
case (value = block.call(yaml))
when String
return value
when nil
return
else
raise TypeError, "Return value of `strategy :electron_builder` block must be a string."
end
end
yaml["version"]
@ -65,7 +70,7 @@ module Homebrew
url: String,
regex: T.nilable(Regexp),
cask: T.nilable(Cask::Cask),
block: T.nilable(T.proc.params(arg0: Hash).returns(String)),
block: T.nilable(T.proc.params(arg0: T::Hash[String, T.untyped]).returns(T.nilable(String))),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask: nil, &block)

View File

@ -56,7 +56,7 @@ module Homebrew
url: String,
regex: T.nilable(Regexp),
cask: Cask::Cask,
block: T.nilable(T.proc.params(arg0: T::Hash[String, Item]).returns(String)),
block: T.nilable(T.proc.params(arg0: T::Hash[String, Item]).returns(T.nilable(String))),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask:, &block)
@ -69,13 +69,14 @@ module Homebrew
versions = unversioned_cask_checker.all_versions.transform_values { |v| Item.new(bundle_version: v) }
if block
match = block.call(versions)
unless T.unsafe(match).is_a?(String)
case (value = block.call(versions))
when String
match_data[:matches][value] = Version.new(value)
when nil
return match_data
else
raise TypeError, "Return value of `strategy :extract_plist` block must be a string."
end
match_data[:matches][match] = Version.new(match) if match
elsif versions.any?
versions.each_value do |item|
version = item.bundle_version.nice_version

View File

@ -81,8 +81,9 @@ module Homebrew
url: String,
regex: T.nilable(Regexp),
cask: T.nilable(Cask::Cask),
block: T.nilable(T.proc.params(arg0: T::Array[String])
.returns(T.any(T::Array[String], String))),
block: T.nilable(
T.proc.params(arg0: T::Array[String]).returns(T.any(String, T::Array[String], NilClass)),
),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask: nil, &block)
@ -102,9 +103,11 @@ module Homebrew
when String
match_data[:matches][value] = Version.new(value)
when Array
value.each do |tag|
value.compact.uniq.each do |tag|
match_data[:matches][tag] = Version.new(tag)
end
when nil
return match_data
else
raise TypeError, "Return value of `strategy :git` block must be a string or array of strings."
end

View File

@ -67,7 +67,9 @@ module Homebrew
url: String,
regex: T.nilable(Regexp),
cask: T.nilable(Cask::Cask),
block: T.nilable(T.proc.params(arg0: String).returns(T.any(T::Array[String], String))),
block: T.nilable(
T.proc.params(arg0: String, arg1: Regexp).returns(T.any(String, T::Array[String], NilClass)),
),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask: nil, &block)

View File

@ -55,7 +55,9 @@ module Homebrew
url: String,
regex: T.nilable(Regexp),
cask: T.nilable(Cask::Cask),
block: T.nilable(T.proc.params(arg0: String).returns(T.any(T::Array[String], String))),
block: T.nilable(
T.proc.params(arg0: String, arg1: Regexp).returns(T.any(String, T::Array[String], NilClass)),
),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask: nil, &block)

View File

@ -59,7 +59,9 @@ module Homebrew
url: String,
regex: T.nilable(Regexp),
cask: T.nilable(Cask::Cask),
block: T.nilable(T.proc.params(arg0: String).returns(T.any(T::Array[String], String))),
block: T.nilable(
T.proc.params(arg0: String, arg1: Regexp).returns(T.any(String, T::Array[String], NilClass)),
),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask: nil, &block)

View File

@ -52,7 +52,9 @@ module Homebrew
url: String,
regex: T.nilable(Regexp),
cask: T.nilable(Cask::Cask),
block: T.nilable(T.proc.params(arg0: String).returns(T.any(T::Array[String], String))),
block: T.nilable(
T.proc.params(arg0: String, arg1: Regexp).returns(T.any(String, T::Array[String], NilClass)),
),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask: nil, &block)

View File

@ -24,6 +24,9 @@ module Homebrew
# The `Regexp` used to determine if the strategy applies to the URL.
URL_MATCH_REGEX = %r{^https?://}i.freeze
# The header fields to check when a `strategy` block isn't provided.
DEFAULT_HEADERS_TO_CHECK = ["content-disposition", "location"].freeze
# Whether the strategy can be applied to the provided URL.
# The strategy will technically match any HTTP URL but is
# only usable with a `livecheck` block containing a regex
@ -40,8 +43,7 @@ module Homebrew
url: String,
regex: T.nilable(Regexp),
cask: T.nilable(Cask::Cask),
block: T.nilable(T.proc.params(arg0: T::Hash[String, String])
.returns(T.any(T::Array[String], String))),
block: T.nilable(T.proc.params(arg0: T::Hash[String, String]).returns(T.nilable(String))),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask: nil, &block)
@ -52,31 +54,34 @@ module Homebrew
# Merge the headers from all responses into one hash
merged_headers = headers.reduce(&:merge)
if block
match = yield merged_headers, regex
version = if block
case (value = block.call(merged_headers, regex))
when String
value
when nil
return match_data
else
raise TypeError, "Return value of `strategy :header_match` block must be a string."
end
else
match = nil
value = nil
DEFAULT_HEADERS_TO_CHECK.each do |header_name|
header_value = merged_headers[header_name]
next if header_value.blank?
if (filename = merged_headers["content-disposition"])
if regex
match ||= filename[regex, 1]
value = header_value[regex, 1]
else
v = Version.parse(filename, detected_from_url: true)
match ||= v.to_s unless v.null?
v = Version.parse(header_value, detected_from_url: true)
value = v.to_s unless v.null?
end
break if value.present?
end
if (location = merged_headers["location"])
if regex
match ||= location[regex, 1]
else
v = Version.parse(location, detected_from_url: true)
match ||= v.to_s unless v.null?
end
end
value
end
match_data[:matches][match] = Version.new(match) if match
match_data[:matches][version] = Version.new(version) if version
match_data
end

View File

@ -50,7 +50,9 @@ module Homebrew
url: String,
regex: T.nilable(Regexp),
cask: T.nilable(Cask::Cask),
block: T.nilable(T.proc.params(arg0: String).returns(T.any(T::Array[String], String))),
block: T.nilable(
T.proc.params(arg0: String, arg1: Regexp).returns(T.any(String, T::Array[String], NilClass)),
),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask: nil, &block)

View File

@ -46,7 +46,9 @@ module Homebrew
url: String,
regex: T.nilable(Regexp),
cask: T.nilable(Cask::Cask),
block: T.nilable(T.proc.params(arg0: String).returns(T.any(T::Array[String], String))),
block: T.nilable(
T.proc.params(arg0: String, arg1: Regexp).returns(T.any(String, T::Array[String], NilClass)),
),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask: nil, &block)

View File

@ -45,13 +45,24 @@ module Homebrew
# @param regex [Regexp] a regex used for matching versions in the
# content
# @return [Array]
sig {
params(
content: String,
regex: Regexp,
block: T.nilable(
T.proc.params(arg0: String, arg1: Regexp).returns(T.any(String, T::Array[String], NilClass)),
),
).returns(T::Array[String])
}
def self.page_matches(content, regex, &block)
if block
case (value = block.call(content, regex))
when String
return [value]
when Array
return value
return value.compact.uniq
when nil
return []
else
raise TypeError, "Return value of `strategy :page_match` block must be a string or array of strings."
end
@ -61,10 +72,10 @@ module Homebrew
case match
when String
match
else
when Array
match.first
end
end.uniq
end.compact.uniq
end
# Checks the content at the URL for new versions, using the provided
@ -78,10 +89,12 @@ module Homebrew
sig {
params(
url: String,
regex: T.nilable(Regexp),
regex: Regexp,
cask: T.nilable(Cask::Cask),
provided_content: T.nilable(String),
block: T.nilable(T.proc.params(arg0: String).returns(T.any(T::Array[String], String))),
block: T.nilable(
T.proc.params(arg0: String, arg1: Regexp).returns(T.any(String, T::Array[String], NilClass)),
),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask: nil, provided_content: nil, &block)

View File

@ -56,7 +56,9 @@ module Homebrew
url: String,
regex: T.nilable(Regexp),
cask: T.nilable(Cask::Cask),
block: T.nilable(T.proc.params(arg0: String).returns(T.any(T::Array[String], String))),
block: T.nilable(
T.proc.params(arg0: String, arg1: Regexp).returns(T.any(String, T::Array[String], NilClass)),
),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask: nil, &block)

View File

@ -62,7 +62,9 @@ module Homebrew
url: String,
regex: T.nilable(Regexp),
cask: T.nilable(Cask::Cask),
block: T.nilable(T.proc.params(arg0: String).returns(T.any(T::Array[String], String))),
block: T.nilable(
T.proc.params(arg0: String, arg1: Regexp).returns(T.any(String, T::Array[String], NilClass)),
),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask: nil, &block)

View File

@ -144,7 +144,7 @@ module Homebrew
url: String,
regex: T.nilable(Regexp),
cask: T.nilable(Cask::Cask),
block: T.nilable(T.proc.params(arg0: Item).returns(String)),
block: T.nilable(T.proc.params(arg0: Item).returns(T.nilable(String))),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask: nil, &block)
@ -156,19 +156,20 @@ module Homebrew
content = match_data.delete(:content)
if (item = item_from_content(content))
match = if block
value = block.call(item)
unless T.unsafe(value).is_a?(String)
version = if block
case (value = block.call(item))
when String
value
when nil
return match_data
else
raise TypeError, "Return value of `strategy :sparkle` block must be a string."
end
value
else
item.bundle_version&.nice_version
end
match_data[:matches][match] = Version.new(match) if match
match_data[:matches][version] = Version.new(version) if version
end
match_data

View File

@ -85,7 +85,9 @@ module Homebrew
url: String,
regex: T.nilable(Regexp),
cask: T.nilable(Cask::Cask),
block: T.nilable(T.proc.params(arg0: String).returns(T.any(T::Array[String], String))),
block: T.nilable(
T.proc.params(arg0: String, arg1: Regexp).returns(T.any(String, T::Array[String], NilClass)),
),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask: nil, &block)

View File

@ -54,5 +54,14 @@ describe Homebrew::Livecheck::Strategy::ElectronBuilder do
expect(version).to eq "1.2.4"
end
it "allows a nil return from a strategy block" do
expect(electron_builder.version_from_content(electron_builder_yaml) { next }).to eq(nil)
end
it "errors on an invalid return type from a strategy block" do
expect { electron_builder.version_from_content(electron_builder_yaml) { 123 } }
.to raise_error(TypeError, "Return value of `strategy :electron_builder` block must be a string.")
end
end
end

View File

@ -72,9 +72,13 @@ describe Homebrew::Livecheck::Strategy::PageMatch do
end
it "finds matching text in page content using a strategy block" do
expect(page_match.page_matches(page_content, regex) { |content| content.scan(regex).map(&:first).uniq })
expect(page_match.page_matches(page_content, regex) { |content, regex| content.scan(regex).map(&:first).uniq })
.to eq(page_content_matches)
end
it "allows a nil return from a strategy block" do
expect(page_match.page_matches(page_content, regex) { next }).to eq([])
end
end
describe "::find_versions?" do