livecheck: move url/regex generation into methods

This commit is contained in:
Sam Ford 2021-07-28 13:20:12 -04:00
parent f026dd21c1
commit 26821301e7
No known key found for this signature in database
GPG Key ID: 95209E46C7FFDEFE
27 changed files with 795 additions and 198 deletions

View File

@ -75,6 +75,9 @@ module Homebrew
# In rare cases, this can also be a double newline (`\n\n`).
HTTP_HEAD_BODY_SEPARATOR = "\r\n\r\n"
# A regex used to identify a tarball extension at the end of a string.
TARBALL_EXTENSION_REGEX = /\.t(?:ar\..+|[a-z0-9]+)$/i.freeze
# An error message to use when a `strategy` block returns a value of
# an inappropriate type.
INVALID_BLOCK_RETURN_VALUE_MSG = "Return value of a strategy block must be a string or array of strings."

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
module Homebrew
@ -42,6 +42,38 @@ module Homebrew
URL_MATCH_REGEX.match?(url)
end
# Extracts information from a provided URL and uses it to generate
# various input values used by the strategy to check for new versions.
# Some of these values act as defaults and can be overridden in a
# `livecheck` block.
#
# @param url [String] the URL used to generate values
# @return [Hash]
sig { params(url: String).returns(T::Hash[Symbol, T.untyped]) }
def self.generate_input_values(url)
values = {}
match = url.match(URL_MATCH_REGEX)
return values if match.blank?
# Example URL: `https://archive.apache.org/dist/example/`
values[:url] = "https://archive.apache.org/dist/#{match[:path]}/"
regex_prefix = Regexp.escape(match[:prefix] || "").gsub("\\-", "-")
# Use `\.t` instead of specific tarball extensions (e.g. .tar.gz)
suffix = match[:suffix]&.sub(Strategy::TARBALL_EXTENSION_REGEX, "\.t")
regex_suffix = Regexp.escape(suffix || "").gsub("\\-", "-")
# Example directory regex: `%r{href=["']?v?(\d+(?:\.\d+)+)/}i`
# Example file regexes:
# * `/href=["']?example-v?(\d+(?:\.\d+)+)\.t/i`
# * `/href=["']?example-v?(\d+(?:\.\d+)+)-bin\.zip/i`
values[:regex] = /href=["']?#{regex_prefix}v?(\d+(?:\.\d+)+)#{regex_suffix}/i
values
end
# Generates a URL and regex (if one isn't provided) and passes them
# to {PageMatch.find_versions} to identify versions in the content.
#
@ -59,21 +91,9 @@ module Homebrew
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url:, regex: nil, **unused, &block)
match = url.match(URL_MATCH_REGEX)
generated = generate_input_values(url)
# Use `\.t` instead of specific tarball extensions (e.g. .tar.gz)
suffix = match[:suffix].sub(/\.t(?:ar\..+|[a-z0-9]+)$/i, "\.t")
# Example URL: `https://archive.apache.org/dist/example/`
page_url = "https://archive.apache.org/dist/#{match[:path]}/"
# Example directory regex: `%r{href=["']?v?(\d+(?:\.\d+)+)/}i`
# Example file regexes:
# * `/href=["']?example-v?(\d+(?:\.\d+)+)\.t/i`
# * `/href=["']?example-v?(\d+(?:\.\d+)+)-bin\.zip/i`
regex ||= /href=["']?#{Regexp.escape(match[:prefix])}v?(\d+(?:\.\d+)+)#{Regexp.escape(suffix)}/i
PageMatch.find_versions(url: page_url, regex: regex, **unused, &block)
T.unsafe(PageMatch).find_versions(url: generated[:url], regex: regex || generated[:regex], **unused, &block)
end
end
end

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
module Homebrew
@ -49,6 +49,42 @@ module Homebrew
URL_MATCH_REGEX.match?(url)
end
# Extracts information from a provided URL and uses it to generate
# various input values used by the strategy to check for new versions.
# Some of these values act as defaults and can be overridden in a
# `livecheck` block.
#
# @param url [String] the URL used to generate values
# @return [Hash]
sig { params(url: String).returns(T::Hash[Symbol, T.untyped]) }
def self.generate_input_values(url)
values = {}
match = url.match(URL_MATCH_REGEX)
return values if match.blank?
# `/get/` archives are Git tag snapshots, so we need to check that tab
# instead of the main `/downloads/` page
values[:url] = if match[:dl_type] == "get"
"https://bitbucket.org/#{match[:path]}/downloads/?tab=tags"
else
"https://bitbucket.org/#{match[:path]}/downloads/"
end
regex_prefix = Regexp.escape(T.must(match[:prefix])).gsub("\\-", "-")
# Use `\.t` instead of specific tarball extensions (e.g. .tar.gz)
suffix = T.must(match[:suffix]).sub(Strategy::TARBALL_EXTENSION_REGEX, "\.t")
regex_suffix = Regexp.escape(suffix).gsub("\\-", "-")
# Example regexes:
# * `/href=.*?v?(\d+(?:\.\d+)+)\.t/i`
# * `/href=.*?example-v?(\d+(?:\.\d+)+)\.t/i`
values[:regex] = /href=.*?#{regex_prefix}v?(\d+(?:\.\d+)+)#{regex_suffix}/i
values
end
# Generates a URL and regex (if one isn't provided) and passes them
# to {PageMatch.find_versions} to identify versions in the content.
#
@ -66,25 +102,9 @@ module Homebrew
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url:, regex: nil, **unused, &block)
match = url.match(URL_MATCH_REGEX)
generated = generate_input_values(url)
# Use `\.t` instead of specific tarball extensions (e.g. .tar.gz)
suffix = match[:suffix].sub(/\.t(?:ar\..+|[a-z0-9]+)$/i, "\.t")
# `/get/` archives are Git tag snapshots, so we need to check that tab
# instead of the main `/downloads/` page
page_url = if match[:dl_type] == "get"
"https://bitbucket.org/#{match[:path]}/downloads/?tab=tags"
else
"https://bitbucket.org/#{match[:path]}/downloads/"
end
# Example regexes:
# * `/href=.*?v?(\d+(?:\.\d+)+)\.t/i`
# * `/href=.*?example-v?(\d+(?:\.\d+)+)\.t/i`
regex ||= /href=.*?#{Regexp.escape(match[:prefix])}v?(\d+(?:\.\d+)+)#{Regexp.escape(suffix)}/i
PageMatch.find_versions(url: page_url, regex: regex, **unused, &block)
T.unsafe(PageMatch).find_versions(url: generated[:url], regex: regex || generated[:regex], **unused, &block)
end
end
end

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
module Homebrew
@ -40,6 +40,35 @@ module Homebrew
URL_MATCH_REGEX.match?(url)
end
# Extracts information from a provided URL and uses it to generate
# various input values used by the strategy to check for new versions.
# Some of these values act as defaults and can be overridden in a
# `livecheck` block.
#
# @param url [String] the URL used to generate values
# @return [Hash]
sig { params(url: String).returns(T::Hash[Symbol, T.untyped]) }
def self.generate_input_values(url)
values = {}
match = url.match(URL_MATCH_REGEX)
return values if match.blank?
# The directory listing page where the archive files are found
values[:url] = "https://cpan.metacpan.org#{match[:path]}"
regex_prefix = Regexp.escape(T.must(match[:prefix])).gsub("\\-", "-")
# Use `\.t` instead of specific tarball extensions (e.g. .tar.gz)
suffix = T.must(match[:suffix]).sub(Strategy::TARBALL_EXTENSION_REGEX, "\.t")
regex_suffix = Regexp.escape(suffix).gsub("\\-", "-")
# Example regex: `/href=.*?Brew[._-]v?(\d+(?:\.\d+)*)\.t/i`
values[:regex] = /href=.*?#{regex_prefix}[._-]v?(\d+(?:\.\d+)*)#{regex_suffix}/i
values
end
# Generates a URL and regex (if one isn't provided) and passes them
# to {PageMatch.find_versions} to identify versions in the content.
#
@ -57,18 +86,9 @@ module Homebrew
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url:, regex: nil, **unused, &block)
match = url.match(URL_MATCH_REGEX)
generated = generate_input_values(url)
# Use `\.t` instead of specific tarball extensions (e.g. .tar.gz)
suffix = match[:suffix].sub(/\.t(?:ar\..+|[a-z0-9]+)$/i, "\.t")
# The directory listing page where the archive files are found
page_url = "https://cpan.metacpan.org#{match[:path]}"
# Example regex: `/href=.*?Brew[._-]v?(\d+(?:\.\d+)*)\.t/i`
regex ||= /href=.*?#{match[:prefix]}[._-]v?(\d+(?:\.\d+)*)#{Regexp.escape(suffix)}/i
PageMatch.find_versions(url: page_url, regex: regex, **unused, &block)
T.unsafe(PageMatch).find_versions(url: generated[:url], regex: regex || generated[:regex], **unused, &block)
end
end
end

View File

@ -30,8 +30,8 @@ module Homebrew
# lowest to highest).
PRIORITY = 8
# The default regex used to naively identify numeric versions from tags
# when a regex isn't provided.
# 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.

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
module Homebrew
@ -48,6 +48,10 @@ module Homebrew
/(?<repository>[^/]+) # The GitHub repository name
}ix.freeze
# The default regex used to identify a version from a tag when a regex
# isn't provided.
DEFAULT_REGEX = %r{href=.*?/tag/v?(\d+(?:\.\d+)+)["' >]}i.freeze
# Whether the strategy can be applied to the provided URL.
#
# @param url [String] the URL to match against
@ -57,6 +61,26 @@ module Homebrew
URL_MATCH_REGEX.match?(url)
end
# Extracts information from a provided URL and uses it to generate
# various input values used by the strategy to check for new versions.
# Some of these values act as defaults and can be overridden in a
# `livecheck` block.
#
# @param url [String] the URL used to generate values
# @return [Hash]
sig { params(url: String).returns(T::Hash[Symbol, T.untyped]) }
def self.generate_input_values(url)
values = {}
match = url.sub(/\.git$/i, "").match(URL_MATCH_REGEX)
return values if match.blank?
# Example URL: `https://github.com/example/example/releases/latest`
values[:url] = "https://github.com/#{match[:username]}/#{match[:repository]}/releases/latest"
values
end
# Generates a URL and regex (if one isn't provided) and passes them
# to {PageMatch.find_versions} to identify versions in the content.
#
@ -74,15 +98,9 @@ module Homebrew
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url:, regex: nil, **unused, &block)
match = url.sub(/\.git$/i, "").match(URL_MATCH_REGEX)
generated = generate_input_values(url)
# Example URL: `https://github.com/example/example/releases/latest`
page_url = "https://github.com/#{match[:username]}/#{match[:repository]}/releases/latest"
# The default regex is the same for all URLs using this strategy
regex ||= %r{href=.*?/tag/v?(\d+(?:\.\d+)+)["' >]}i
PageMatch.find_versions(url: page_url, regex: regex, **unused, &block)
T.unsafe(PageMatch).find_versions(url: generated[:url], regex: regex || DEFAULT_REGEX, **unused, &block)
end
end
end

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
module Homebrew
@ -45,6 +45,32 @@ module Homebrew
URL_MATCH_REGEX.match?(url)
end
# Extracts information from a provided URL and uses it to generate
# various input values used by the strategy to check for new versions.
# Some of these values act as defaults and can be overridden in a
# `livecheck` block.
#
# @param url [String] the URL used to generate values
# @return [Hash]
sig { params(url: String).returns(T::Hash[Symbol, T.untyped]) }
def self.generate_input_values(url)
values = {}
match = url.match(URL_MATCH_REGEX)
return values if match.blank?
values[:url] = "https://download.gnome.org/sources/#{match[:package_name]}/cache.json"
regex_name = Regexp.escape(T.must(match[:package_name])).gsub("\\-", "-")
# GNOME archive files seem to use a standard filename format, so we
# count on the delimiter between the package name and numeric
# version being a hyphen and the file being a tarball.
values[:regex] = /#{regex_name}-(\d+(?:\.\d+)+)\.t/i
values
end
# Generates a URL and regex (if one isn't provided) and passes them
# to {PageMatch.find_versions} to identify versions in the content.
#
@ -62,27 +88,24 @@ module Homebrew
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url:, regex: nil, **unused, &block)
match = url.match(URL_MATCH_REGEX)
generated = generate_input_values(url)
page_url = "https://download.gnome.org/sources/#{match[:package_name]}/cache.json"
version_data = T.unsafe(PageMatch).find_versions(
url: generated[:url],
regex: regex || generated[:regex],
**unused,
&block
)
if regex.blank?
# GNOME archive files seem to use a standard filename format, so we
# count on the delimiter between the package name and numeric
# version being a hyphen and the file being a tarball.
regex = /#{Regexp.escape(match[:package_name])}-(\d+(?:\.\d+)+)\.t/i
version_data = PageMatch.find_versions(url: page_url, regex: regex, **unused, &block)
# Filter out unstable versions using the old version scheme where
# the major version is below 40.
version_data[:matches].reject! do |_, version|
version.major < 40 && (version.minor >= 90 || version.minor.to_i.odd?)
end
end
version_data
else
PageMatch.find_versions(url: page_url, regex: regex, **unused, &block)
end
end
end
end

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
module Homebrew
@ -49,6 +49,38 @@ module Homebrew
URL_MATCH_REGEX.match?(url) && url.exclude?("savannah.")
end
# Extracts information from a provided URL and uses it to generate
# various input values used by the strategy to check for new versions.
# Some of these values act as defaults and can be overridden in a
# `livecheck` block.
#
# @param url [String] the URL used to generate values
# @return [Hash]
sig { params(url: String).returns(T::Hash[Symbol, T.untyped]) }
def self.generate_input_values(url)
values = {}
match = url.match(URL_MATCH_REGEX)
return values if match.blank?
# The directory listing page for the project's files
values[:url] = "http://ftp.gnu.org/gnu/#{match[:project_name]}/"
regex_name = Regexp.escape(T.must(match[:project_name])).gsub("\\-", "-")
# The default regex consists of the following parts:
# * `href=.*?`: restricts matching to URLs in `href` attributes
# * The project name
# * `[._-]`: the generic delimiter between project name and version
# * `v?(\d+(?:\.\d+)*)`: the numeric version
# * `(?:\.[a-z]+|/)`: the file extension (a trailing delimiter)
#
# Example regex: `%r{href=.*?example[._-]v?(\d+(?:\.\d+)*)(?:\.[a-z]+|/)}i`
values[:regex] = %r{href=.*?#{regex_name}[._-]v?(\d+(?:\.\d+)*)(?:\.[a-z]+|/)}i
values
end
# Generates a URL and regex (if one isn't provided) and passes them
# to {PageMatch.find_versions} to identify versions in the content.
#
@ -66,22 +98,9 @@ module Homebrew
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url:, regex: nil, **unused, &block)
match = url.match(URL_MATCH_REGEX)
generated = generate_input_values(url)
# The directory listing page for the project's files
page_url = "http://ftp.gnu.org/gnu/#{match[:project_name]}/?C=M&O=D"
# The default regex consists of the following parts:
# * `href=.*?`: restricts matching to URLs in `href` attributes
# * The project name
# * `[._-]`: the generic delimiter between project name and version
# * `v?(\d+(?:\.\d+)*)`: the numeric version
# * `(?:\.[a-z]+|/)`: the file extension (a trailing delimiter)
#
# 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(url: page_url, regex: regex, **unused, &block)
T.unsafe(PageMatch).find_versions(url: generated[:url], regex: regex || generated[:regex], **unused, &block)
end
end
end

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
module Homebrew
@ -42,6 +42,31 @@ module Homebrew
URL_MATCH_REGEX.match?(url)
end
# Extracts information from a provided URL and uses it to generate
# various input values used by the strategy to check for new versions.
# Some of these values act as defaults and can be overridden in a
# `livecheck` block.
#
# @param url [String] the URL used to generate values
# @return [Hash]
sig { params(url: String).returns(T::Hash[Symbol, T.untyped]) }
def self.generate_input_values(url)
values = {}
match = File.basename(url).match(FILENAME_REGEX)
return values if match.blank?
# A page containing a directory listing of the latest source tarball
values[:url] = "https://hackage.haskell.org/package/#{match[:package_name]}/src/"
regex_name = Regexp.escape(T.must(match[:package_name])).gsub("\\-", "-")
# Example regex: `%r{<h3>example-(.*?)/?</h3>}i`
values[:regex] = %r{<h3>#{regex_name}-(.*?)/?</h3>}i
values
end
# Generates a URL and regex (if one isn't provided) and passes them
# to {PageMatch.find_versions} to identify versions in the content.
#
@ -59,15 +84,9 @@ module Homebrew
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url:, regex: nil, **unused, &block)
match = File.basename(url).match(FILENAME_REGEX)
generated = generate_input_values(url)
# A page containing a directory listing of the latest source tarball
page_url = "https://hackage.haskell.org/package/#{match[:package_name]}/src/"
# Example regex: `%r{<h3>example-(.*?)/?</h3>}i`
regex ||= %r{<h3>#{Regexp.escape(match[:package_name])}-(.*?)/?</h3>}i
PageMatch.find_versions(url: page_url, regex: regex, **unused, &block)
T.unsafe(PageMatch).find_versions(url: generated[:url], regex: regex || generated[:regex], **unused, &block)
end
end
end

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
module Homebrew
@ -31,6 +31,10 @@ module Homebrew
/(?<project_name>[^/]+) # The Launchpad project name
}ix.freeze
# The default regex used to identify the latest version when a regex
# isn't provided.
DEFAULT_REGEX = %r{class="[^"]*version[^"]*"[^>]*>\s*Latest version is (.+)\s*</}.freeze
# Whether the strategy can be applied to the provided URL.
#
# @param url [String] the URL to match against
@ -40,6 +44,26 @@ module Homebrew
URL_MATCH_REGEX.match?(url)
end
# Extracts information from a provided URL and uses it to generate
# various input values used by the strategy to check for new versions.
# Some of these values act as defaults and can be overridden in a
# `livecheck` block.
#
# @param url [String] the URL used to generate values
# @return [Hash]
sig { params(url: String).returns(T::Hash[Symbol, T.untyped]) }
def self.generate_input_values(url)
values = {}
match = url.match(URL_MATCH_REGEX)
return values if match.blank?
# The main page for the project on Launchpad
values[:url] = "https://launchpad.net/#{match[:project_name]}/"
values
end
# Generates a URL and regex (if one isn't provided) and passes them
# to {PageMatch.find_versions} to identify versions in the content.
#
@ -57,15 +81,9 @@ module Homebrew
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url:, regex: nil, **unused, &block)
match = url.match(URL_MATCH_REGEX)
generated = generate_input_values(url)
# The main page for the project on Launchpad
page_url = "https://launchpad.net/#{match[:project_name]}"
# The default regex is the same for all URLs using this strategy
regex ||= %r{class="[^"]*version[^"]*"[^>]*>\s*Latest version is (.+)\s*</}
PageMatch.find_versions(url: page_url, regex: regex, **unused, &block)
T.unsafe(PageMatch).find_versions(url: generated[:url], regex: regex || DEFAULT_REGEX, **unused, &block)
end
end
end

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
module Homebrew
@ -36,6 +36,32 @@ module Homebrew
URL_MATCH_REGEX.match?(url)
end
# Extracts information from a provided URL and uses it to generate
# various input values used by the strategy to check for new versions.
# Some of these values act as defaults and can be overridden in a
# `livecheck` block.
#
# @param url [String] the URL used to generate values
# @return [Hash]
sig { params(url: String).returns(T::Hash[Symbol, T.untyped]) }
def self.generate_input_values(url)
values = {}
match = url.match(URL_MATCH_REGEX)
return values if match.blank?
values[:url] = "https://www.npmjs.com/package/#{match[:package_name]}?activeTab=versions"
regex_name = Regexp.escape(T.must(match[:package_name])).gsub("\\-", "-")
# Example regexes:
# * `%r{href=.*?/package/example/v/(\d+(?:\.\d+)+)"}i`
# * `%r{href=.*?/package/@example/example/v/(\d+(?:\.\d+)+)"}i`
values[:regex] = %r{href=.*?/package/#{regex_name}/v/(\d+(?:\.\d+)+)"}i
values
end
# Generates a URL and regex (if one isn't provided) and passes them
# to {PageMatch.find_versions} to identify versions in the content.
#
@ -53,16 +79,9 @@ module Homebrew
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url:, regex: nil, **unused, &block)
match = url.match(URL_MATCH_REGEX)
generated = generate_input_values(url)
page_url = "https://www.npmjs.com/package/#{match[:package_name]}?activeTab=versions"
# Example regexes:
# * `%r{href=.*?/package/example/v/(\d+(?:\.\d+)+)"}i`
# * `%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(url: page_url, regex: regex, **unused, &block)
T.unsafe(PageMatch).find_versions(url: generated[:url], regex: regex || generated[:regex], **unused, &block)
end
end
end

View File

@ -90,6 +90,7 @@ module Homebrew
}
def self.find_versions(url:, regex:, provided_content: nil, **_unused, &block)
match_data = { matches: {}, regex: regex, url: url }
return match_data if url.blank? || regex.blank?
content = if provided_content.is_a?(String)
match_data[:cached] = true

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
module Homebrew
@ -46,6 +46,36 @@ module Homebrew
URL_MATCH_REGEX.match?(url)
end
# Extracts information from a provided URL and uses it to generate
# various input values used by the strategy to check for new versions.
# Some of these values act as defaults and can be overridden in a
# `livecheck` block.
#
# @param url [String] the URL used to generate values
# @return [Hash]
sig { params(url: String).returns(T::Hash[Symbol, T.untyped]) }
def self.generate_input_values(url)
values = {}
match = File.basename(url).match(FILENAME_REGEX)
return values if match.blank?
# It's not technically necessary to have the `#files` fragment at the
# end of the URL but it makes the debug output a bit more useful.
values[:url] = "https://pypi.org/project/#{T.must(match[:package_name]).gsub(/%20|_/, "-")}/#files"
# Use `\.t` instead of specific tarball extensions (e.g. .tar.gz)
suffix = T.must(match[:suffix]).sub(Strategy::TARBALL_EXTENSION_REGEX, "\.t")
regex_suffix = Regexp.escape(suffix).gsub("\\-", "-")
# Example regex: `%r{href=.*?/packages.*?/example[._-]v?(\d+(?:\.\d+)*(?:[._-]post\d+)?)\.t}i`
regex_name = Regexp.escape(T.must(match[:package_name])).gsub("\\-", "-")
values[:regex] =
%r{href=.*?/packages.*?/#{regex_name}[._-]v?(\d+(?:\.\d+)*(?:[._-]post\d+)?)#{regex_suffix}}i
values
end
# Generates a URL and regex (if one isn't provided) and passes them
# to {PageMatch.find_versions} to identify versions in the content.
#
@ -63,21 +93,9 @@ module Homebrew
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url:, regex: nil, **unused, &block)
match = File.basename(url).match(FILENAME_REGEX)
generated = generate_input_values(url)
# Use `\.t` instead of specific tarball extensions (e.g. .tar.gz)
suffix = match[:suffix].sub(/\.t(?:ar\..+|[a-z0-9]+)$/i, "\.t")
# It's not technically necessary to have the `#files` fragment at the
# end of the URL but it makes the debug output a bit more useful.
page_url = "https://pypi.org/project/#{match[:package_name].gsub(/%20|_/, "-")}/#files"
# Example regex: `%r{href=.*?/packages.*?/example[._-]v?(\d+(?:\.\d+)*(?:[._-]post\d+)?)\.t}i`
re_package_name = Regexp.escape(match[:package_name])
re_suffix = Regexp.escape(suffix)
regex ||= %r{href=.*?/packages.*?/#{re_package_name}[._-]v?(\d+(?:\.\d+)*(?:[._-]post\d+)?)#{re_suffix}}i
PageMatch.find_versions(url: page_url, regex: regex, **unused, &block)
T.unsafe(PageMatch).find_versions(url: generated[:url], regex: regex || generated[:regex], **unused, &block)
end
end
end

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
module Homebrew
@ -52,6 +52,35 @@ module Homebrew
URL_MATCH_REGEX.match?(url)
end
# Extracts information from a provided URL and uses it to generate
# various input values used by the strategy to check for new versions.
# Some of these values act as defaults and can be overridden in a
# `livecheck` block.
#
# @param url [String] the URL used to generate values
# @return [Hash]
sig { params(url: String).returns(T::Hash[Symbol, T.untyped]) }
def self.generate_input_values(url)
values = {}
match = url.match(URL_MATCH_REGEX)
return values if match.blank?
# Don't generate a URL if the URL already points to the RSS feed
unless url.match?(%r{/rss(?:/?$|\?)})
values[:url] = "https://sourceforge.net/projects/#{match[:project_name]}/rss"
end
regex_name = Regexp.escape(T.must(match[:project_name])).gsub("\\-", "-")
# It may be possible to improve the generated regex but there's quite
# a bit of variation between projects and it can be challenging to
# create something that works for most URLs.
values[:regex] = %r{url=.*?/#{regex_name}/files/.*?[-_/](\d+(?:[-.]\d+)+)[-_/%.]}i
values
end
# Generates a URL and regex (if one isn't provided) and passes them
# to {PageMatch.find_versions} to identify versions in the content.
#
@ -69,20 +98,14 @@ module Homebrew
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url:, regex: nil, **unused, &block)
match = url.match(URL_MATCH_REGEX)
generated = generate_input_values(url)
page_url = if url.match?(%r{/rss(?:/?$|\?)})
url
else
"https://sourceforge.net/projects/#{match[:project_name]}/rss"
end
# It may be possible to improve the default regex but there's quite a
# bit of variation between projects and it can be challenging to
# create something that works for most URLs.
regex ||= %r{url=.*?/#{Regexp.escape(match[:project_name])}/files/.*?[-_/](\d+(?:[-.]\d+)+)[-_/%.]}i
PageMatch.find_versions(url: page_url, regex: regex, **unused, &block)
T.unsafe(PageMatch).find_versions(
url: generated[:url] || url,
regex: regex || generated[:regex],
**unused,
&block
)
end
end
end

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
module Homebrew
@ -69,6 +69,34 @@ module Homebrew
URL_MATCH_REGEX.match?(url)
end
# Extracts information from a provided URL and uses it to generate
# various input values used by the strategy to check for new versions.
# Some of these values act as defaults and can be overridden in a
# `livecheck` block.
#
# @param url [String] the URL used to generate values
# @return [Hash]
sig { params(url: String).returns(T::Hash[Symbol, T.untyped]) }
def self.generate_input_values(url)
values = {}
file_name = File.basename(url)
match = file_name.match(FILENAME_REGEX)
return values if match.blank?
# /pub/ URLs redirect to the same URL with /archive/, so we replace
# it to avoid the redirection. Removing the filename from the end of
# the URL gives us the relevant directory listing page.
values[:url] = url.sub("x.org/pub/", "x.org/archive/").delete_suffix(file_name)
regex_name = Regexp.escape(T.must(match[:module_name])).gsub("\\-", "-")
# Example regex: `/href=.*?example[._-]v?(\d+(?:\.\d+)+)\.t/i`
values[:regex] = /href=.*?#{regex_name}[._-]v?(\d+(?:\.\d+)+)\.t/i
values
end
# Generates a URL and regex (if one isn't provided) and checks the
# content at the URL for new versions (using the regex for matching).
#
@ -92,29 +120,21 @@ module Homebrew
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url:, regex: nil, **unused, &block)
file_name = File.basename(url)
match = file_name.match(FILENAME_REGEX)
# /pub/ URLs redirect to the same URL with /archive/, so we replace
# it to avoid the redirection. Removing the filename from the end of
# the URL gives us the relevant directory listing page.
page_url = url.sub("x.org/pub/", "x.org/archive/").delete_suffix(file_name)
# Example regex: `/href=.*?example[._-]v?(\d+(?:\.\d+)+)\.t/i`
regex ||= /href=.*?#{Regexp.escape(match[:module_name])}[._-]v?(\d+(?:\.\d+)+)\.t/i
generated = generate_input_values(url)
generated_url = generated[:url]
# Use the cached page content to avoid duplicate fetches
cached_content = @page_data[page_url]
match_data = PageMatch.find_versions(
url: page_url,
regex: regex,
cached_content = @page_data[generated_url]
match_data = T.unsafe(PageMatch).find_versions(
url: generated_url,
regex: regex || generated[:regex],
provided_content: cached_content,
**unused,
&block
)
# Cache any new page content
@page_data[page_url] = match_data[:content] if match_data[:content].present?
@page_data[generated_url] = match_data[:content] if match_data[:content].present?
match_data
end

View File

@ -1,21 +1,58 @@
# typed: false
# frozen_string_literal: true
require "livecheck/strategy/apache"
require "livecheck/strategy"
describe Homebrew::Livecheck::Strategy::Apache do
subject(:apache) { described_class }
let(:apache_url) { "https://www.apache.org/dyn/closer.lua?path=abc/1.2.3/def-1.2.3.tar.gz" }
let(:apache_urls) {
{
version_dir: "https://www.apache.org/dyn/closer.lua?path=abc/1.2.3/def-1.2.3.tar.gz",
name_and_version_dir: "https://www.apache.org/dyn/closer.lua?path=abc/def-1.2.3/ghi-1.2.3.tar.gz",
name_dir_bin: "https://www.apache.org/dyn/closer.lua?path=abc/def/ghi-1.2.3-bin.tar.gz",
}
}
let(:non_apache_url) { "https://brew.sh/test" }
let(:generated) {
{
version_dir: {
url: "https://archive.apache.org/dist/abc/",
regex: %r{href=["']?v?(\d+(?:\.\d+)+)/}i,
},
name_and_version_dir: {
url: "https://archive.apache.org/dist/abc/",
regex: %r{href=["']?def-v?(\d+(?:\.\d+)+)/}i,
},
name_dir_bin: {
url: "https://archive.apache.org/dist/abc/def/",
regex: /href=["']?ghi-v?(\d+(?:\.\d+)+)-bin\.t/i,
},
}
}
describe "::match?" do
it "returns true for an Apache URL" do
expect(apache.match?(apache_url)).to be true
expect(apache.match?(apache_urls[:version_dir])).to be true
expect(apache.match?(apache_urls[:name_and_version_dir])).to be true
expect(apache.match?(apache_urls[:name_dir_bin])).to be true
end
it "returns false for a non-Apache URL" do
expect(apache.match?(non_apache_url)).to be false
end
end
describe "::generate_input_values" do
it "returns a hash containing url and regex for an Apache URL" do
expect(apache.generate_input_values(apache_urls[:version_dir])).to eq(generated[:version_dir])
expect(apache.generate_input_values(apache_urls[:name_and_version_dir])).to eq(generated[:name_and_version_dir])
expect(apache.generate_input_values(apache_urls[:name_dir_bin])).to eq(generated[:name_dir_bin])
end
it "returns an empty hash for a non-Apache URL" do
expect(apache.generate_input_values(non_apache_url)).to eq({})
end
end
end

View File

@ -1,21 +1,51 @@
# typed: false
# frozen_string_literal: true
require "livecheck/strategy/bitbucket"
require "livecheck/strategy"
describe Homebrew::Livecheck::Strategy::Bitbucket do
subject(:bitbucket) { described_class }
let(:bitbucket_url) { "https://bitbucket.org/abc/def/get/1.2.3.tar.gz" }
let(:bitbucket_urls) {
{
get: "https://bitbucket.org/abc/def/get/1.2.3.tar.gz",
downloads: "https://bitbucket.org/abc/def/downloads/ghi-1.2.3.tar.gz",
}
}
let(:non_bitbucket_url) { "https://brew.sh/test" }
let(:generated) {
{
get: {
url: "https://bitbucket.org/abc/def/downloads/?tab=tags",
regex: /href=.*?v?(\d+(?:\.\d+)+)\.t/i,
},
downloads: {
url: "https://bitbucket.org/abc/def/downloads/",
regex: /href=.*?ghi-v?(\d+(?:\.\d+)+)\.t/i,
},
}
}
describe "::match?" do
it "returns true for a Bitbucket URL" do
expect(bitbucket.match?(bitbucket_url)).to be true
expect(bitbucket.match?(bitbucket_urls[:get])).to be true
expect(bitbucket.match?(bitbucket_urls[:downloads])).to be true
end
it "returns false for a non-Bitbucket URL" do
expect(bitbucket.match?(non_bitbucket_url)).to be false
end
end
describe "::generate_input_values" do
it "returns a hash containing url and regex for a Bitbucket URL" do
expect(bitbucket.generate_input_values(bitbucket_urls[:get])).to eq(generated[:get])
expect(bitbucket.generate_input_values(bitbucket_urls[:downloads])).to eq(generated[:downloads])
end
it "returns an empty hash for a non-Bitbucket URL" do
expect(bitbucket.generate_input_values(non_bitbucket_url)).to eq({})
end
end
end

View File

@ -1,23 +1,51 @@
# typed: false
# frozen_string_literal: true
require "livecheck/strategy/cpan"
require "livecheck/strategy"
describe Homebrew::Livecheck::Strategy::Cpan do
subject(:cpan) { described_class }
let(:cpan_url_no_subdirectory) { "https://cpan.metacpan.org/authors/id/H/HO/HOMEBREW/Brew-v1.2.3.tar.gz" }
let(:cpan_url_with_subdirectory) { "https://cpan.metacpan.org/authors/id/H/HO/HOMEBREW/brew/brew-v1.2.3.tar.gz" }
let(:cpan_urls) {
{
no_subdirectory: "https://cpan.metacpan.org/authors/id/H/HO/HOMEBREW/Brew-v1.2.3.tar.gz",
with_subdirectory: "https://cpan.metacpan.org/authors/id/H/HO/HOMEBREW/brew/brew-v1.2.3.tar.gz",
}
}
let(:non_cpan_url) { "https://brew.sh/test" }
let(:generated) {
{
no_subdirectory: {
url: "https://cpan.metacpan.org/authors/id/H/HO/HOMEBREW/",
regex: /href=.*?Brew[._-]v?(\d+(?:\.\d+)*)\.t/i,
},
with_subdirectory: {
url: "https://cpan.metacpan.org/authors/id/H/HO/HOMEBREW/brew/",
regex: /href=.*?brew[._-]v?(\d+(?:\.\d+)*)\.t/i,
},
}
}
describe "::match?" do
it "returns true for a CPAN URL" do
expect(cpan.match?(cpan_url_no_subdirectory)).to be true
expect(cpan.match?(cpan_url_with_subdirectory)).to be true
expect(cpan.match?(cpan_urls[:no_subdirectory])).to be true
expect(cpan.match?(cpan_urls[:with_subdirectory])).to be true
end
it "returns false for a non-CPAN URL" do
expect(cpan.match?(non_cpan_url)).to be false
end
end
describe "::generate_input_values" do
it "returns a hash containing url and regex for a CPAN URL" do
expect(cpan.generate_input_values(cpan_urls[:no_subdirectory])).to eq(generated[:no_subdirectory])
expect(cpan.generate_input_values(cpan_urls[:with_subdirectory])).to eq(generated[:with_subdirectory])
end
it "returns an empty hash for a non-CPAN URL" do
expect(cpan.generate_input_values(non_cpan_url)).to eq({})
end
end
end

View File

@ -6,28 +6,54 @@ require "livecheck/strategy/github_latest"
describe Homebrew::Livecheck::Strategy::GithubLatest do
subject(:github_latest) { described_class }
let(:github_release_artifact_url) {
"https://github.com/example/example/releases/download/1.2.3/example-1.2.3.tar.gz"
let(:github_urls) {
{
release_artifact: "https://github.com/abc/def/releases/download/1.2.3/ghi-1.2.3.tar.gz",
tag_archive: "https://github.com/abc/def/archive/v1.2.3.tar.gz",
repository_upload: "https://github.com/downloads/abc/def/ghi-1.2.3.tar.gz",
}
}
let(:github_tag_archive_url) { "https://github.com/example/example/archive/v1.2.3.tar.gz" }
let(:github_repository_upload_url) { "https://github.com/downloads/example/example/example-1.2.3.tar.gz" }
let(:non_github_url) { "https://brew.sh/test" }
let(:generated) {
{
url: "https://github.com/abc/def/releases/latest",
}
}
describe "::match?" do
it "returns true for a GitHub release artifact URL" do
expect(github_latest.match?(github_release_artifact_url)).to be true
expect(github_latest.match?(github_urls[:release_artifact])).to be true
end
it "returns true for a GitHub tag archive URL" do
expect(github_latest.match?(github_tag_archive_url)).to be true
expect(github_latest.match?(github_urls[:tag_archive])).to be true
end
it "returns true for a GitHub repository upload URL" do
expect(github_latest.match?(github_repository_upload_url)).to be true
expect(github_latest.match?(github_urls[:repository_upload])).to be true
end
it "returns false for a non-GitHub URL" do
expect(github_latest.match?(non_github_url)).to be false
end
end
describe "::generate_input_values" do
it "returns a hash containing a url and regex for a GitHub release artifact URL" do
expect(github_latest.generate_input_values(github_urls[:release_artifact])).to eq(generated)
end
it "returns a hash containing a url and regex for a GitHub tag archive URL" do
expect(github_latest.generate_input_values(github_urls[:tag_archive])).to eq(generated)
end
it "returns a hash containing a url and regex for a GitHub repository upload URL" do
expect(github_latest.generate_input_values(github_urls[:repository_upload])).to eq(generated)
end
it "returns an empty hash for a non-Github URL" do
expect(github_latest.generate_input_values(non_github_url)).to eq({})
end
end
end

View File

@ -6,9 +6,16 @@ require "livecheck/strategy/gnome"
describe Homebrew::Livecheck::Strategy::Gnome do
subject(:gnome) { described_class }
let(:gnome_url) { "https://download.gnome.org/sources/abc/1.2/def-1.2.3.tar.xz" }
let(:gnome_url) { "https://download.gnome.org/sources/abc/1.2/abc-1.2.3.tar.xz" }
let(:non_gnome_url) { "https://brew.sh/test" }
let(:generated) {
{
url: "https://download.gnome.org/sources/abc/cache.json",
regex: /abc-(\d+(?:\.\d+)+)\.t/i,
}
}
describe "::match?" do
it "returns true for a GNOME URL" do
expect(gnome.match?(gnome_url)).to be true
@ -18,4 +25,14 @@ describe Homebrew::Livecheck::Strategy::Gnome do
expect(gnome.match?(non_gnome_url)).to be false
end
end
describe "::generate_input_values" do
it "returns a hash containing url and regex for a GNOME URL" do
expect(gnome.generate_input_values(gnome_url)).to eq(generated)
end
it "returns an empty hash for a non-GNOME URL" do
expect(gnome.generate_input_values(non_gnome_url)).to eq({})
end
end
end

View File

@ -6,21 +6,63 @@ require "livecheck/strategy/gnu"
describe Homebrew::Livecheck::Strategy::Gnu do
subject(:gnu) { described_class }
let(:gnu_url) { "https://ftp.gnu.org/gnu/abc/def-1.2.3.tar.gz" }
let(:savannah_gnu_url) { "https://download.savannah.gnu.org/releases/abc/def-1.2.3.tar.gz" }
let(:gnu_urls) {
{
no_version_dir: "https://ftp.gnu.org/gnu/abc/abc-1.2.3.tar.gz",
software_page: "https://www.gnu.org/software/abc/",
subdomain: "https://abc.gnu.org",
savannah: "https://download.savannah.gnu.org/releases/abc/abc-1.2.3.tar.gz",
}
}
let(:non_gnu_url) { "https://brew.sh/test" }
let(:generated) {
{
no_version_dir: {
url: "http://ftp.gnu.org/gnu/abc/",
regex: %r{href=.*?abc[._-]v?(\d+(?:\.\d+)*)(?:\.[a-z]+|/)}i,
},
software_page: {
url: "http://ftp.gnu.org/gnu/abc/",
regex: %r{href=.*?abc[._-]v?(\d+(?:\.\d+)*)(?:\.[a-z]+|/)}i,
},
subdomain: {
url: "http://ftp.gnu.org/gnu/abc/",
regex: %r{href=.*?abc[._-]v?(\d+(?:\.\d+)*)(?:\.[a-z]+|/)}i,
},
savannah: {},
}
}
describe "::match?" do
it "returns true for a [non-Savannah] GNU URL" do
expect(gnu.match?(gnu_url)).to be true
expect(gnu.match?(gnu_urls[:no_version_dir])).to be true
expect(gnu.match?(gnu_urls[:software_page])).to be true
expect(gnu.match?(gnu_urls[:subdomain])).to be true
end
it "returns false for a Savannah GNU URL" do
expect(gnu.match?(savannah_gnu_url)).to be false
expect(gnu.match?(gnu_urls[:savannah])).to be false
end
it "returns false for a non-GNU URL (not nongnu.org)" do
expect(gnu.match?(non_gnu_url)).to be false
end
end
describe "::generate_input_values" do
it "returns a hash containing url and regex for a [non-Savannah] GNU URL" do
expect(gnu.generate_input_values(gnu_urls[:no_version_dir])).to eq(generated[:no_version_dir])
expect(gnu.generate_input_values(gnu_urls[:software_page])).to eq(generated[:software_page])
expect(gnu.generate_input_values(gnu_urls[:subdomain])).to eq(generated[:subdomain])
end
it "returns an empty hash for a Savannah GNU URL" do
expect(gnu.generate_input_values(gnu_urls[:savannah])).to eq(generated[:savannah])
end
it "returns an empty hash for a non-GNU URL (not nongnu.org)" do
expect(gnu.generate_input_values(non_gnu_url)).to eq({})
end
end
end

View File

@ -6,18 +6,40 @@ require "livecheck/strategy/hackage"
describe Homebrew::Livecheck::Strategy::Hackage do
subject(:hackage) { described_class }
let(:hackage_url) { "https://hackage.haskell.org/package/abc-1.2.3/def-1.2.3.tar.gz" }
let(:hackage_downloads_url) { "https://downloads.haskell.org/~abc/1.2.3/def-1.2.3-src.tar.xz" }
let(:hackage_urls) {
{
package: "https://hackage.haskell.org/package/abc-1.2.3/abc-1.2.3.tar.gz",
downloads: "https://downloads.haskell.org/~abc/1.2.3/abc-1.2.3-src.tar.xz",
}
}
let(:non_hackage_url) { "https://brew.sh/test" }
let(:generated) {
{
url: "https://hackage.haskell.org/package/abc/src/",
regex: %r{<h3>abc-(.*?)/?</h3>}i,
}
}
describe "::match?" do
it "returns true for a Hackage URL" do
expect(hackage.match?(hackage_url)).to be true
expect(hackage.match?(hackage_downloads_url)).to be true
expect(hackage.match?(hackage_urls[:package])).to be true
expect(hackage.match?(hackage_urls[:downloads])).to be true
end
it "returns false for a non-Hackage URL" do
expect(hackage.match?(non_hackage_url)).to be false
end
end
describe "::generate_input_values" do
it "returns a hash containing url and regex for a Hackage URL" do
expect(hackage.generate_input_values(hackage_urls[:package])).to eq(generated)
expect(hackage.generate_input_values(hackage_urls[:downloads])).to eq(generated)
end
it "returns an empty hash for a non-Hackage URL" do
expect(hackage.generate_input_values(non_hackage_url)).to eq({})
end
end
end

View File

@ -6,16 +6,42 @@ require "livecheck/strategy/launchpad"
describe Homebrew::Livecheck::Strategy::Launchpad do
subject(:launchpad) { described_class }
let(:launchpad_url) { "https://launchpad.net/abc/1.2/1.2.3/+download/def-1.2.3.tar.gz" }
let(:launchpad_urls) {
{
version_dir: "https://launchpad.net/abc/1.2/1.2.3/+download/abc-1.2.3.tar.gz",
trunk: "https://launchpad.net/abc/trunk/1.2.3/+download/abc-1.2.3.tar.gz",
code_subdomain: "https://code.launchpad.net/abc/1.2/1.2.3/+download/abc-1.2.3.tar.gz",
}
}
let(:non_launchpad_url) { "https://brew.sh/test" }
let(:generated) {
{
url: "https://launchpad.net/abc/",
}
}
describe "::match?" do
it "returns true for a Launchpad URL" do
expect(launchpad.match?(launchpad_url)).to be true
expect(launchpad.match?(launchpad_urls[:version_dir])).to be true
expect(launchpad.match?(launchpad_urls[:trunk])).to be true
expect(launchpad.match?(launchpad_urls[:code_subdomain])).to be true
end
it "returns false for a non-Launchpad URL" do
expect(launchpad.match?(non_launchpad_url)).to be false
end
end
describe "::generate_input_values" do
it "returns a hash containing url and regex for an Launchpad URL" do
expect(launchpad.generate_input_values(launchpad_urls[:version_dir])).to eq(generated)
expect(launchpad.generate_input_values(launchpad_urls[:trunk])).to eq(generated)
expect(launchpad.generate_input_values(launchpad_urls[:code_subdomain])).to eq(generated)
end
it "returns an empty hash for a non-Launchpad URL" do
expect(launchpad.generate_input_values(non_launchpad_url)).to eq({})
end
end
end

View File

@ -6,18 +6,46 @@ require "livecheck/strategy/npm"
describe Homebrew::Livecheck::Strategy::Npm do
subject(:npm) { described_class }
let(:npm_url) { "https://registry.npmjs.org/abc/-/def-1.2.3.tgz" }
let(:npm_scoped_url) { "https://registry.npmjs.org/@example/abc/-/def-1.2.3.tgz" }
let(:npm_urls) {
{
typical: "https://registry.npmjs.org/abc/-/def-1.2.3.tgz",
org_scoped: "https://registry.npmjs.org/@example/abc/-/def-1.2.3.tgz",
}
}
let(:non_npm_url) { "https://brew.sh/test" }
let(:generated) {
{
typical: {
url: "https://www.npmjs.com/package/abc?activeTab=versions",
regex: %r{href=.*?/package/abc/v/(\d+(?:\.\d+)+)"}i,
},
org_scoped: {
url: "https://www.npmjs.com/package/@example/abc?activeTab=versions",
regex: %r{href=.*?/package/@example/abc/v/(\d+(?:\.\d+)+)"}i,
},
}
}
describe "::match?" do
it "returns true for an npm URL" do
expect(npm.match?(npm_url)).to be true
expect(npm.match?(npm_scoped_url)).to be true
expect(npm.match?(npm_urls[:typical])).to be true
expect(npm.match?(npm_urls[:org_scoped])).to be true
end
it "returns false for a non-npm URL" do
expect(npm.match?(non_npm_url)).to be false
end
end
describe "::generate_input_values" do
it "returns a hash containing url and regex for an npm URL" do
expect(npm.generate_input_values(npm_urls[:typical])).to eq(generated[:typical])
expect(npm.generate_input_values(npm_urls[:org_scoped])).to eq(generated[:org_scoped])
end
it "returns an empty hash for a non-npm URL" do
expect(npm.generate_input_values(non_npm_url)).to eq({})
end
end
end

View File

@ -1,14 +1,21 @@
# typed: false
# frozen_string_literal: true
require "livecheck/strategy/pypi"
require "livecheck/strategy"
describe Homebrew::Livecheck::Strategy::Pypi do
subject(:pypi) { described_class }
let(:pypi_url) { "https://files.pythonhosted.org/packages/ab/cd/efg/hij-1.2.3.tar.gz" }
let(:pypi_url) { "https://files.pythonhosted.org/packages/ab/cd/efg/example-1.2.3.tar.gz" }
let(:non_pypi_url) { "https://brew.sh/test" }
let(:generated) {
{
url: "https://pypi.org/project/example/#files",
regex: %r{href=.*?/packages.*?/example[._-]v?(\d+(?:\.\d+)*(?:[._-]post\d+)?)\.t}i,
}
}
describe "::match?" do
it "returns true for a PyPI URL" do
expect(pypi.match?(pypi_url)).to be true
@ -18,4 +25,14 @@ describe Homebrew::Livecheck::Strategy::Pypi do
expect(pypi.match?(non_pypi_url)).to be false
end
end
describe "::generate_input_values" do
it "returns a hash containing url and regex for an PyPI URL" do
expect(pypi.generate_input_values(pypi_url)).to eq(generated)
end
it "returns an empty hash for a non-PyPI URL" do
expect(pypi.generate_input_values(non_pypi_url)).to eq({})
end
end
end

View File

@ -6,16 +6,48 @@ require "livecheck/strategy/sourceforge"
describe Homebrew::Livecheck::Strategy::Sourceforge do
subject(:sourceforge) { described_class }
let(:sourceforge_url) { "https://downloads.sourceforge.net/project/abc/def-1.2.3.tar.gz" }
let(:sourceforge_urls) {
{
typical: "https://downloads.sourceforge.net/project/abc/def-1.2.3.tar.gz",
rss: "https://sourceforge.net/projects/abc/rss",
rss_with_path: "https://sourceforge.net/projects/abc/rss?path=/def",
}
}
let(:non_sourceforge_url) { "https://brew.sh/test" }
let(:generated) {
{
typical: {
url: "https://sourceforge.net/projects/abc/rss",
regex: %r{url=.*?/abc/files/.*?[-_/](\d+(?:[-.]\d+)+)[-_/%.]}i,
},
rss: {
regex: %r{url=.*?/abc/files/.*?[-_/](\d+(?:[-.]\d+)+)[-_/%.]}i,
},
}
}
describe "::match?" do
it "returns true for a SourceForge URL" do
expect(sourceforge.match?(sourceforge_url)).to be true
expect(sourceforge.match?(sourceforge_urls[:typical])).to be true
expect(sourceforge.match?(sourceforge_urls[:rss])).to be true
expect(sourceforge.match?(sourceforge_urls[:rss_with_path])).to be true
end
it "returns false for a non-SourceForge URL" do
expect(sourceforge.match?(non_sourceforge_url)).to be false
end
end
describe "::generate_input_values" do
it "returns a hash containing url and regex for an Apache URL" do
expect(sourceforge.generate_input_values(sourceforge_urls[:typical])).to eq(generated[:typical])
expect(sourceforge.generate_input_values(sourceforge_urls[:rss])).to eq(generated[:rss])
expect(sourceforge.generate_input_values(sourceforge_urls[:rss_with_path])).to eq(generated[:rss])
end
it "returns an empty hash for a non-Apache URL" do
expect(sourceforge.generate_input_values(non_sourceforge_url)).to eq({})
end
end
end

View File

@ -6,16 +6,67 @@ require "livecheck/strategy/xorg"
describe Homebrew::Livecheck::Strategy::Xorg do
subject(:xorg) { described_class }
let(:xorg_url) { "https://www.x.org/archive/individual/app/abc-1.2.3.tar.bz2" }
let(:xorg_urls) {
{
app: "https://www.x.org/archive/individual/app/abc-1.2.3.tar.bz2",
font: "https://www.x.org/archive/individual/font/abc-1.2.3.tar.bz2",
lib: "https://www.x.org/archive/individual/lib/libabc-1.2.3.tar.bz2",
ftp_lib: "https://ftp.x.org/archive/individual/lib/libabc-1.2.3.tar.bz2",
pub_doc: "https://www.x.org/pub/individual/doc/abc-1.2.3.tar.bz2",
}
}
let(:non_xorg_url) { "https://brew.sh/test" }
let(:generated) {
{
app: {
url: "https://www.x.org/archive/individual/app/",
regex: /href=.*?abc[._-]v?(\d+(?:\.\d+)+)\.t/i,
},
font: {
url: "https://www.x.org/archive/individual/font/",
regex: /href=.*?abc[._-]v?(\d+(?:\.\d+)+)\.t/i,
},
lib: {
url: "https://www.x.org/archive/individual/lib/",
regex: /href=.*?libabc[._-]v?(\d+(?:\.\d+)+)\.t/i,
},
ftp_lib: {
url: "https://ftp.x.org/archive/individual/lib/",
regex: /href=.*?libabc[._-]v?(\d+(?:\.\d+)+)\.t/i,
},
pub_doc: {
url: "https://www.x.org/archive/individual/doc/",
regex: /href=.*?abc[._-]v?(\d+(?:\.\d+)+)\.t/i,
},
}
}
describe "::match?" do
it "returns true for an X.Org URL" do
expect(xorg.match?(xorg_url)).to be true
expect(xorg.match?(xorg_urls[:app])).to be true
expect(xorg.match?(xorg_urls[:font])).to be true
expect(xorg.match?(xorg_urls[:lib])).to be true
expect(xorg.match?(xorg_urls[:ftp_lib])).to be true
expect(xorg.match?(xorg_urls[:pub_doc])).to be true
end
it "returns false for a non-X.Org URL" do
expect(xorg.match?(non_xorg_url)).to be false
end
end
describe "::generate_input_values" do
it "returns a hash containing url and regex for an X.org URL" do
expect(xorg.generate_input_values(xorg_urls[:app])).to eq(generated[:app])
expect(xorg.generate_input_values(xorg_urls[:font])).to eq(generated[:font])
expect(xorg.generate_input_values(xorg_urls[:lib])).to eq(generated[:lib])
expect(xorg.generate_input_values(xorg_urls[:ftp_lib])).to eq(generated[:ftp_lib])
expect(xorg.generate_input_values(xorg_urls[:pub_doc])).to eq(generated[:pub_doc])
end
it "returns an empty hash for a non-X.org URL" do
expect(xorg.generate_input_values(non_xorg_url)).to eq({})
end
end
end