Add extract_plist
strategy.
This commit is contained in:
parent
6d0275ff57
commit
a210b1a04e
@ -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
|
||||
|
||||
|
@ -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) }),
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 }
|
||||
|
91
Library/Homebrew/livecheck/strategy/extract_plist.rb
Normal file
91
Library/Homebrew/livecheck/strategy/extract_plist.rb
Normal 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
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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?
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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?
|
||||
|
Loading…
x
Reference in New Issue
Block a user