Add extract_plist strategy.

This commit is contained in:
Markus Reiter 2021-04-04 03:00:34 +02:00
parent 6d0275ff57
commit a210b1a04e
No known key found for this signature in database
GPG Key ID: 245293B51702655B
24 changed files with 379 additions and 77 deletions

View File

@ -17,7 +17,11 @@ module Homebrew
sig { params(info_plist_path: Pathname).returns(T.nilable(T.attached_class)) }
def self.from_info_plist(info_plist_path)
plist = system_command!("plutil", args: ["-convert", "xml1", "-o", "-", info_plist_path]).plist
from_info_plist_content(plist)
end
sig { params(plist: T::Hash[String, T.untyped]).returns(T.nilable(T.attached_class)) }
def self.from_info_plist_content(plist)
short_version = plist["CFBundleShortVersionString"].presence
version = plist["CFBundleVersion"].presence

View File

@ -501,8 +501,7 @@ module Homebrew
regex_provided: livecheck_regex.present?,
block_provided: livecheck.strategy_block.present?,
)
strategy = Strategy.from_symbol(livecheck_strategy)
strategy ||= strategies.first
strategy = Strategy.from_symbol(livecheck_strategy) || strategies.first
strategy_name = livecheck_strategy_names[strategy]
if debug
@ -514,24 +513,29 @@ module Homebrew
puts "Regex: #{livecheck_regex.inspect}" if livecheck_regex.present?
end
if livecheck_strategy == :page_match && (livecheck_regex.blank? && livecheck.strategy_block.blank?)
odebug "#{strategy_name} strategy requires a regex or block"
next
end
if livecheck_strategy.present? && livecheck_url.blank?
odebug "#{strategy_name} strategy requires a URL"
next
end
if livecheck_strategy.present? && strategies.exclude?(strategy)
odebug "#{strategy_name} strategy does not apply to this URL"
next
if livecheck_strategy.present?
if livecheck_strategy == :page_match && (livecheck_regex.blank? && livecheck.strategy_block.blank?)
odebug "#{strategy_name} strategy requires a regex or block"
next
elsif livecheck_url.blank?
odebug "#{strategy_name} strategy requires a URL"
next
elsif strategies.exclude?(strategy)
odebug "#{strategy_name} strategy does not apply to this URL"
next
end
end
next if strategy.blank?
strategy_data = strategy.find_versions(url, livecheck_regex, &livecheck.strategy_block)
strategy_data = begin
strategy.find_versions(url, livecheck_regex, cask: cask, &livecheck.strategy_block)
rescue ArgumentError => e
raise unless e.message.include?("unknown keyword: cask")
odeprecated "`def self.find_versions` in `#{strategy}` without a `cask` parameter"
strategy.find_versions(url, livecheck_regex, &livecheck.strategy_block)
end
match_version_map = strategy_data[:matches]
regex = strategy_data[:regex]
messages = strategy_data[:messages]
@ -559,7 +563,9 @@ module Homebrew
end
end
if debug && match_version_map.present?
next if match_version_map.blank?
if debug
puts
puts "Matched Versions:"
@ -572,8 +578,6 @@ module Homebrew
end
end
next if match_version_map.blank?
version_info = {
latest: Version.new(match_version_map.values.max_by { |v| LivecheckVersion.create(formula_or_cask, v) }),
}

View File

@ -148,6 +148,7 @@ require_relative "strategy/apache"
require_relative "strategy/bitbucket"
require_relative "strategy/cpan"
require_relative "strategy/electron_builder"
require_relative "strategy/extract_plist"
require_relative "strategy/git"
require_relative "strategy/github_latest"
require_relative "strategy/gnome"

View File

@ -21,6 +21,8 @@ module Homebrew
#
# @api public
class Apache
extend T::Sig
# The `Regexp` used to determine if the strategy applies to the URL.
URL_MATCH_REGEX = %r{
^https?://www\.apache\.org
@ -45,7 +47,15 @@ module Homebrew
# @param url [String] the URL of the content to check
# @param regex [Regexp] a regex used for matching versions in content
# @return [Hash]
def self.find_versions(url, regex = nil, &block)
sig {
params(
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))),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask: nil, &block)
match = url.match(URL_MATCH_REGEX)
# Use `\.t` instead of specific tarball extensions (e.g. .tar.gz)
@ -60,7 +70,7 @@ module Homebrew
# * `/href=["']?example-v?(\d+(?:\.\d+)+)-bin\.zip/i`
regex ||= /href=["']?#{Regexp.escape(match[:prefix])}v?(\d+(?:\.\d+)+)#{Regexp.escape(suffix)}/i
PageMatch.find_versions(page_url, regex, &block)
PageMatch.find_versions(page_url, regex, cask: cask, &block)
end
end
end

View File

@ -28,6 +28,8 @@ module Homebrew
#
# @api public
class Bitbucket
extend T::Sig
# The `Regexp` used to determine if the strategy applies to the URL.
URL_MATCH_REGEX = %r{
^https?://bitbucket\.org
@ -52,7 +54,15 @@ module Homebrew
# @param url [String] the URL of the content to check
# @param regex [Regexp] a regex used for matching versions in content
# @return [Hash]
def self.find_versions(url, regex = nil, &block)
sig {
params(
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))),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask: nil, &block)
match = url.match(URL_MATCH_REGEX)
# Use `\.t` instead of specific tarball extensions (e.g. .tar.gz)
@ -71,7 +81,7 @@ module Homebrew
# * `/href=.*?example-v?(\d+(?:\.\d+)+)\.t/i`
regex ||= /href=.*?#{Regexp.escape(match[:prefix])}v?(\d+(?:\.\d+)+)#{Regexp.escape(suffix)}/i
PageMatch.find_versions(page_url, regex, &block)
PageMatch.find_versions(page_url, regex, cask: cask, &block)
end
end
end

View File

@ -18,6 +18,8 @@ module Homebrew
#
# @api public
class Cpan
extend T::Sig
NICE_NAME = "CPAN"
# The `Regexp` used to determine if the strategy applies to the URL.
@ -43,7 +45,15 @@ module Homebrew
# @param url [String] the URL of the content to check
# @param regex [Regexp] a regex used for matching versions in content
# @return [Hash]
def self.find_versions(url, regex = nil, &block)
sig {
params(
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))),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask: nil, &block)
match = url.match(URL_MATCH_REGEX)
# Use `\.t` instead of specific tarball extensions (e.g. .tar.gz)
@ -55,7 +65,7 @@ module Homebrew
# Example regex: `/href=.*?Brew[._-]v?(\d+(?:\.\d+)*)\.t/i`
regex ||= /href=.*?#{match[:prefix]}[._-]v?(\d+(?:\.\d+)*)#{Regexp.escape(suffix)}/i
PageMatch.find_versions(page_url, regex, &block)
PageMatch.find_versions(page_url, regex, cask: cask, &block)
end
end
end

View File

@ -64,10 +64,11 @@ module Homebrew
params(
url: String,
regex: T.nilable(Regexp),
cask: T.nilable(Cask::Cask),
block: T.nilable(T.proc.params(arg0: Hash).returns(String)),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex = nil, &block)
def self.find_versions(url, regex, cask: nil, &block)
raise ArgumentError, "The #{T.must(name).demodulize} strategy does not support a regex." if regex
match_data = { matches: {}, regex: regex, url: url }

View File

@ -0,0 +1,91 @@
# typed: true
# frozen_string_literal: true
require "bundle_version"
require "unversioned_cask_checker"
require_relative "page_match"
module Homebrew
module Livecheck
module Strategy
# The {ExtractPlist} strategy downloads the file at a URL and
# extracts versions from contained `.plist` files.
#
# @api private
class ExtractPlist
extend T::Sig
# A priority of zero causes livecheck to skip the strategy. We only
# apply {ExtractPlist} using `strategy :extract_plist` in a `livecheck` block,
# as we can't automatically determine when this can be successfully
# applied to a URL without fetching the content.
PRIORITY = 0
# The `Regexp` used to determine if the strategy applies to the URL.
URL_MATCH_REGEX = %r{^https?://}i.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
# or block.
sig { params(url: String).returns(T::Boolean) }
def self.match?(url)
URL_MATCH_REGEX.match?(url)
end
# @api private
Item = Struct.new(
# @api private
:bundle_version,
keyword_init: true,
) do
extend T::Sig
extend Forwardable
# @api public
delegate version: :bundle_version
# @api public
delegate short_version: :bundle_version
end
# Checks the content at the URL for new versions.
sig {
params(
url: String,
regex: T.nilable(Regexp),
cask: Cask::Cask,
block: T.nilable(T.proc.params(arg0: T::Hash[String, Item]).returns(String)),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask:, &block)
raise ArgumentError, "The #{T.must(name).demodulize} strategy does not support a regex." if regex
raise ArgumentError, "The #{T.must(name).demodulize} strategy only supports casks." unless T.unsafe(cask)
match_data = { matches: {}, regex: regex, url: url }
unversioned_cask_checker = UnversionedCaskChecker.new(cask)
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)
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
match_data[:matches][version] = Version.new(version)
end
end
match_data
end
end
end
end
end

View File

@ -24,6 +24,8 @@ module Homebrew
#
# @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
@ -74,7 +76,16 @@ module Homebrew
# @param url [String] the URL of the Git repository to check
# @param regex [Regexp] the regex to use for matching versions
# @return [Hash]
def self.find_versions(url, regex = nil, &block)
sig {
params(
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))),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask: nil, &block)
match_data = { matches: {}, regex: regex, url: url }
tags_data = tag_info(url, regex)

View File

@ -32,6 +32,8 @@ module Homebrew
#
# @api public
class GithubLatest
extend T::Sig
NICE_NAME = "GitHub - Latest"
# A priority of zero causes livecheck to skip the strategy. We do this
@ -60,7 +62,15 @@ module Homebrew
# @param url [String] the URL of the content to check
# @param regex [Regexp] a regex used for matching versions in content
# @return [Hash]
def self.find_versions(url, regex = nil, &block)
sig {
params(
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))),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask: nil, &block)
match = url.sub(/\.git$/i, "").match(URL_MATCH_REGEX)
# Example URL: `https://github.com/example/example/releases/latest`
@ -69,7 +79,7 @@ module Homebrew
# The default regex is the same for all URLs using this strategy
regex ||= %r{href=.*?/tag/v?(\d+(?:\.\d+)+)["' >]}i
PageMatch.find_versions(page_url, regex, &block)
PageMatch.find_versions(page_url, regex, cask: cask, &block)
end
end
end

View File

@ -17,6 +17,8 @@ module Homebrew
#
# @api public
class Gnome
extend T::Sig
NICE_NAME = "GNOME"
# The `Regexp` used to determine if the strategy applies to the URL.
@ -40,7 +42,15 @@ module Homebrew
# @param url [String] the URL of the content to check
# @param regex [Regexp] a regex used for matching versions in content
# @return [Hash]
def self.find_versions(url, regex = nil, &block)
sig {
params(
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))),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask: nil, &block)
match = url.match(URL_MATCH_REGEX)
page_url = "https://download.gnome.org/sources/#{match[:package_name]}/cache.json"
@ -57,7 +67,7 @@ module Homebrew
# Example regex: `/example-(\d+\.([0-8]\d*?)?[02468](?:\.\d+)*?)\.t/i`
regex ||= /#{Regexp.escape(match[:package_name])}-(\d+\.([0-8]\d*?)?[02468](?:\.\d+)*?)\.t/i
PageMatch.find_versions(page_url, regex, &block)
PageMatch.find_versions(page_url, regex, cask: cask, &block)
end
end
end

View File

@ -29,6 +29,8 @@ module Homebrew
#
# @api public
class Gnu
extend T::Sig
NICE_NAME = "GNU"
# The `Regexp` used to determine if the strategy applies to the URL.
@ -52,7 +54,15 @@ module Homebrew
# @param url [String] the URL of the content to check
# @param regex [Regexp] a regex used for matching versions in content
# @return [Hash]
def self.find_versions(url, regex = nil, &block)
sig {
params(
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))),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask: nil, &block)
match = url.match(URL_MATCH_REGEX)
# The directory listing page for the project's files
@ -68,7 +78,7 @@ module Homebrew
# Example regex: `%r{href=.*?example[._-]v?(\d+(?:\.\d+)*)(?:\.[a-z]+|/)}i`
regex ||= %r{href=.*?#{match[:project_name]}[._-]v?(\d+(?:\.\d+)*)(?:\.[a-z]+|/)}i
PageMatch.find_versions(page_url, regex, &block)
PageMatch.find_versions(page_url, regex, cask: cask, &block)
end
end
end

View File

@ -17,6 +17,8 @@ module Homebrew
#
# @api public
class Hackage
extend T::Sig
# A `Regexp` used in determining if the strategy applies to the URL and
# also as part of extracting the package name from the URL basename.
PACKAGE_NAME_REGEX = /(?<package_name>.+?)-\d+/i.freeze
@ -45,7 +47,15 @@ module Homebrew
# @param url [String] the URL of the content to check
# @param regex [Regexp] a regex used for matching versions in content
# @return [Hash]
def self.find_versions(url, regex = nil, &block)
sig {
params(
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))),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask: nil, &block)
match = File.basename(url).match(FILENAME_REGEX)
# A page containing a directory listing of the latest source tarball
@ -54,7 +64,7 @@ module Homebrew
# Example regex: `%r{<h3>example-(.*?)/?</h3>}i`
regex ||= %r{<h3>#{Regexp.escape(match[:package_name])}-(.*?)/?</h3>}i
PageMatch.find_versions(page_url, regex, &block)
PageMatch.find_versions(page_url, regex, cask: cask, &block)
end
end
end

View File

@ -35,8 +35,16 @@ module Homebrew
# Checks the final URL for new versions after following all redirections,
# using the provided regex for matching.
sig { params(url: String, regex: T.nilable(Regexp)).returns(T::Hash[Symbol, T.untyped]) }
def self.find_versions(url, regex, &block)
sig {
params(
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))),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask: nil, &block)
match_data = { matches: {}, regex: regex, url: url }
headers = Strategy.page_headers(url)
@ -45,7 +53,7 @@ module Homebrew
merged_headers = headers.reduce(&:merge)
if block
match = block.call(merged_headers, regex)
match = yield merged_headers, regex
else
match = nil

View File

@ -23,6 +23,8 @@ module Homebrew
#
# @api public
class Launchpad
extend T::Sig
# The `Regexp` used to determine if the strategy applies to the URL.
URL_MATCH_REGEX = %r{
^https?://(?:[^/]+?\.)*launchpad\.net
@ -43,7 +45,15 @@ module Homebrew
# @param url [String] the URL of the content to check
# @param regex [Regexp] a regex used for matching versions in content
# @return [Hash]
def self.find_versions(url, regex = nil, &block)
sig {
params(
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))),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask: nil, &block)
match = url.match(URL_MATCH_REGEX)
# The main page for the project on Launchpad
@ -52,7 +62,7 @@ module Homebrew
# The default regex is the same for all URLs using this strategy
regex ||= %r{class="[^"]*version[^"]*"[^>]*>\s*Latest version is (.+)\s*</}
PageMatch.find_versions(page_url, regex, &block)
PageMatch.find_versions(page_url, regex, cask: cask, &block)
end
end
end

View File

@ -17,6 +17,8 @@ module Homebrew
#
# @api public
class Npm
extend T::Sig
NICE_NAME = "npm"
# The `Regexp` used to determine if the strategy applies to the URL.
@ -39,7 +41,15 @@ module Homebrew
# @param url [String] the URL of the content to check
# @param regex [Regexp] a regex used for matching versions in content
# @return [Hash]
def self.find_versions(url, regex = nil, &block)
sig {
params(
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))),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask: nil, &block)
match = url.match(URL_MATCH_REGEX)
page_url = "https://www.npmjs.com/package/#{match[:package_name]}?activeTab=versions"
@ -49,7 +59,7 @@ module Homebrew
# * `%r{href=.*?/package/@example/example/v/(\d+(?:\.\d+)+)"}i`
regex ||= %r{href=.*?/package/#{Regexp.escape(match[:package_name])}/v/(\d+(?:\.\d+)+)"}i
PageMatch.find_versions(page_url, regex, &block)
PageMatch.find_versions(page_url, regex, cask: cask, &block)
end
end
end

View File

@ -81,11 +81,12 @@ module Homebrew
params(
url: String,
regex: T.nilable(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))),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, provided_content = nil, &block)
def self.find_versions(url, regex, cask: nil, provided_content: nil, &block)
match_data = { matches: {}, regex: regex, url: url }
content = if provided_content.is_a?(String)

View File

@ -17,6 +17,8 @@ module Homebrew
#
# @api public
class Pypi
extend T::Sig
NICE_NAME = "PyPI"
# The `Regexp` used to extract the package name and suffix (e.g., file
@ -49,7 +51,15 @@ module Homebrew
# @param url [String] the URL of the content to check
# @param regex [Regexp] a regex used for matching versions in content
# @return [Hash]
def self.find_versions(url, regex = nil, &block)
sig {
params(
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))),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask: nil, &block)
match = File.basename(url).match(FILENAME_REGEX)
# Use `\.t` instead of specific tarball extensions (e.g. .tar.gz)
@ -64,7 +74,7 @@ module Homebrew
re_suffix = Regexp.escape(suffix)
regex ||= %r{href=.*?/packages.*?/#{re_package_name}[._-]v?(\d+(?:\.\d+)*(?:[._-]post\d+)?)#{re_suffix}}i
PageMatch.find_versions(page_url, regex, &block)
PageMatch.find_versions(page_url, regex, cask: cask, &block)
end
end
end

View File

@ -31,6 +31,8 @@ module Homebrew
#
# @api public
class Sourceforge
extend T::Sig
NICE_NAME = "SourceForge"
# The `Regexp` used to determine if the strategy applies to the URL.
@ -55,7 +57,15 @@ module Homebrew
# @param url [String] the URL of the content to check
# @param regex [Regexp] a regex used for matching versions in content
# @return [Hash]
def self.find_versions(url, regex = nil, &block)
sig {
params(
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))),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask: nil, &block)
match = url.match(URL_MATCH_REGEX)
page_url = "https://sourceforge.net/projects/#{match[:project_name]}/rss"
@ -65,7 +75,7 @@ module Homebrew
# create something that works for most URLs.
regex ||= %r{url=.*?/#{Regexp.escape(match[:project_name])}/files/.*?[-_/](\d+(?:[-.]\d+)+)[-_/%.]}i
PageMatch.find_versions(page_url, regex, &block)
PageMatch.find_versions(page_url, regex, cask: cask, &block)
end
end
end

View File

@ -32,12 +32,24 @@ module Homebrew
URL_MATCH_REGEX.match?(url)
end
Item = Struct.new(:title, :url, :bundle_version, :short_version, :version, keyword_init: true) do
# @api private
Item = Struct.new(
# @api public
:title,
# @api public
:url,
# @api private
:bundle_version,
keyword_init: true,
) do
extend T::Sig
extend Forwardable
# @api public
delegate version: :bundle_version
# @api public
delegate short_version: :bundle_version
end
@ -74,8 +86,6 @@ module Homebrew
title: title,
url: url,
bundle_version: bundle_version,
short_version: bundle_version&.short_version,
version: bundle_version&.version,
}.compact
Item.new(**data) unless data.empty?
@ -85,8 +95,15 @@ module Homebrew
end
# Checks the content at the URL for new versions.
sig { params(url: String, regex: T.nilable(Regexp)).returns(T::Hash[Symbol, T.untyped]) }
def self.find_versions(url, regex, &block)
sig {
params(
url: String,
regex: T.nilable(Regexp),
cask: T.nilable(Cask::Cask),
block: T.nilable(T.proc.params(arg0: Item).returns(String)),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask: nil, &block)
raise ArgumentError, "The #{T.must(name).demodulize} strategy does not support a regex." if regex
match_data = { matches: {}, regex: regex, url: url }
@ -96,7 +113,13 @@ module Homebrew
if (item = item_from_content(content))
match = if block
block.call(item)&.to_s
value = block.call(item)
unless T.unsafe(value).is_a?(String)
raise TypeError, "Return value of `strategy :sparkle` block must be a string."
end
value
else
item.bundle_version&.nice_version
end

View File

@ -38,6 +38,8 @@ module Homebrew
#
# @api public
class Xorg
extend T::Sig
NICE_NAME = "X.Org"
# A `Regexp` used in determining if the strategy applies to the URL and
@ -78,7 +80,15 @@ module Homebrew
# @param url [String] the URL of the content to check
# @param regex [Regexp] a regex used for matching versions in content
# @return [Hash]
def self.find_versions(url, regex = nil, &block)
sig {
params(
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))),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url, regex, cask: nil, &block)
file_name = File.basename(url)
match = file_name.match(FILENAME_REGEX)
@ -92,7 +102,7 @@ module Homebrew
# Use the cached page content to avoid duplicate fetches
cached_content = @page_data[page_url]
match_data = PageMatch.find_versions(page_url, regex, cached_content, &block)
match_data = PageMatch.find_versions(page_url, regex, provided_content: cached_content, cask: cask, &block)
# Cache any new page content
@page_data[page_url] = match_data[:content] if match_data[:content].present?

View File

@ -79,7 +79,8 @@ describe Homebrew::Livecheck::Strategy::PageMatch do
describe "::find_versions?" do
it "finds versions in provided_content" do
expect(page_match.find_versions(url, regex, page_content)).to eq(find_versions_cached_return_hash)
expect(page_match.find_versions(url, regex, provided_content: page_content))
.to eq(find_versions_cached_return_hash)
end
end
end

View File

@ -10,26 +10,13 @@ describe Homebrew::Livecheck::Strategy::Sparkle do
let(:appcast_data) {
{
title: "Version 1.2.3",
url: "https://www.example.com/example/example.tar.gz",
bundle_version: Homebrew::BundleVersion.new("1.2.3", "1234"),
short_version: "1.2.3",
version: "1234",
title: "Version 1.2.3",
url: "https://www.example.com/example/example.tar.gz",
short_version: "1.2.3",
version: "1234",
}
}
let(:appcast_item) {
Homebrew::Livecheck::Strategy::Sparkle::Item.new(
{
title: appcast_data[:title],
url: appcast_data[:url],
bundle_version: appcast_data[:bundle_version],
short_version: appcast_data[:bundle_version]&.short_version,
version: appcast_data[:bundle_version]&.version,
},
)
}
let(:appcast_xml) {
<<~EOS
<?xml version="1.0" encoding="utf-8"?>
@ -65,10 +52,10 @@ describe Homebrew::Livecheck::Strategy::Sparkle do
it "returns an Item when given XML data" do
expect(item_from_appcast_xml).to be_a(Homebrew::Livecheck::Strategy::Sparkle::Item)
expect(item_from_appcast_xml.title).to eq(appcast_item.title)
expect(item_from_appcast_xml.url).to eq(appcast_item.url)
expect(item_from_appcast_xml.bundle_version.short_version).to eq(appcast_item.bundle_version.short_version)
expect(item_from_appcast_xml.bundle_version.version).to eq(appcast_item.bundle_version.version)
expect(item_from_appcast_xml.title).to eq(appcast_data[:title])
expect(item_from_appcast_xml.url).to eq(appcast_data[:url])
expect(item_from_appcast_xml.short_version).to eq(appcast_data[:short_version])
expect(item_from_appcast_xml.version).to eq(appcast_data[:version])
end
end
end

View File

@ -60,6 +60,56 @@ module Homebrew
end
end
sig { returns(T::Hash[String, BundleVersion]) }
def all_versions
versions = {}
parse_info_plist = proc do |info_plist_path|
plist = system_command!("plutil", args: ["-convert", "xml1", "-o", "-", info_plist_path]).plist
id = plist["CFBundleIdentifier"]
version = BundleVersion.from_info_plist_content(plist)
versions[id] = version if id && version
end
Dir.mktmpdir do |dir|
dir = Pathname(dir)
installer.extract_primary_container(to: dir)
info_plist_paths = apps.flat_map do |app|
top_level_info_plists(Pathname.glob(dir/"**"/app.source.basename/"Contents"/"Info.plist")).sort
end
info_plist_paths.each(&parse_info_plist)
pkg_paths = pkgs.flat_map do |pkg|
Pathname.glob(dir/"**"/pkg.path.basename).sort
end
pkg_paths.each do |pkg_path|
Dir.mktmpdir do |extract_dir|
extract_dir = Pathname(extract_dir)
FileUtils.rmdir extract_dir
system_command! "pkgutil", args: ["--expand-full", pkg_path, extract_dir]
top_level_info_plist_paths = top_level_info_plists(Pathname.glob(extract_dir/"**/Contents/Info.plist"))
top_level_info_plist_paths.each(&parse_info_plist)
ensure
Cask::Utils.gain_permissions_remove(extract_dir)
extract_dir.mkpath
end
end
nil
end
versions
end
sig { returns(T.nilable(String)) }
def guess_cask_version
if apps.empty? && pkgs.empty?