-
-
Notifications
You must be signed in to change notification settings - Fork 10.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
4ef6a2c
commit 00e0492
Showing
3 changed files
with
155 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
# typed: true | ||
# frozen_string_literal: true | ||
|
||
module Homebrew | ||
module Livecheck | ||
module Strategy | ||
# The {Github} strategy identifies versions of software at | ||
# github.com by checking a repository's "latest" release page. | ||
# | ||
# GitHub URLs take a few different formats: | ||
# | ||
# * `https://github.com/example/example/releases/download/1.2.3/example-1.2.3.tar.gz` | ||
# * `https://github.com/example/example/archive/v1.2.3.tar.gz` | ||
# * `https://github.com/downloads/example/example/example-1.2.3.tar.gz` | ||
# | ||
# A repository's `/releases/latest` URL normally redirects to a release | ||
# tag (e.g., `/releases/tag/1.2.3`). When there isn't a "latest" release, | ||
# it will redirect to the `/releases` page. | ||
# | ||
# This strategy should only be used when we know the upstream repository | ||
# has a "latest" release and the tagged release is appropriate to use | ||
# (e.g., "latest" isn't wrongly pointing to an unstable version, not | ||
# picking up the actual latest version, etc.). The strategy can only be | ||
# applied by using `strategy :github_latest` in a `livecheck` block. | ||
# | ||
# The default regex identifies versions like `1.2.3`/`v1.2.3` in `href` | ||
# attributes containing the tag URL (e.g., | ||
# `/example/example/releases/tag/v1.2.3`). This is a common tag format | ||
# but a modified regex can be provided in a `livecheck` block to override | ||
# the default if a repository uses a different format (e.g., | ||
# `example-1.2.3`, `1.2.3d`, `1.2.3-4`, etc.). | ||
# | ||
# @api public | ||
class GithubRelease | ||
extend T::Sig | ||
|
||
NICE_NAME = "GitHub - Releases" | ||
|
||
# A priority of zero causes livecheck to skip the strategy. We do this | ||
# for {GithubLatest} so we can selectively apply the strategy using | ||
# `strategy :github_latest` in a `livecheck` block. | ||
PRIORITY = 9 | ||
|
||
# The `Regexp` used to determine if the strategy applies to the URL. | ||
URL_MATCH_REGEX = %r{ | ||
^https?://github\.com | ||
/(?:downloads/)?(?<username>[^/]+) # The GitHub username | ||
/(?<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 = /v?(\d+(?:\.\d+)+)/i.freeze | ||
|
||
# Keys in the JSON that could contain the version. | ||
VERSION_KEYS = ["name", "tag_name"].freeze | ||
|
||
# Whether the strategy can be applied to the provided URL. | ||
# | ||
# @param url [String] the URL to match against | ||
# @return [Boolean] | ||
sig { params(url: String).returns(T::Boolean) } | ||
def self.match?(url) | ||
URL_MATCH_REGEX.match?(url) | ||
end | ||
|
||
# Uses the regex to match text in the content or, if a block is | ||
# provided, passes the page content to the block to handle matching. | ||
# With either approach, an array of unique matches is returned. | ||
# | ||
# @param content [String] the page content to check | ||
# @param regex [Regexp, nil] a regex used for matching versions in the | ||
# content | ||
# @return [Array] | ||
sig { | ||
params( | ||
content: T::Array[T::Hash[String, T.untyped]], | ||
regex: T.nilable(Regexp), | ||
block: T.nilable(Proc), | ||
).returns(T::Array[String]) | ||
} | ||
def self.versions_from_content(content, regex, &block) | ||
if block | ||
block_return_value = regex.present? ? yield(content, regex) : yield(content) | ||
return Strategy.handle_block_return(block_return_value) | ||
end | ||
|
||
values = [] | ||
content.each do |release| | ||
next if release.blank? | ||
|
||
VERSION_KEYS.each do |key| | ||
match = release[key].match(regex) | ||
|
||
next if match.blank? | ||
|
||
values.push(match.to_s) | ||
break | ||
end | ||
end | ||
|
||
values.compact.uniq | ||
end | ||
|
||
# Generates a URL and regex (if one isn't provided) and passes them | ||
# to {PageMatch.find_versions} to identify versions in the content. | ||
# | ||
# @param url [String] the URL of the content to check | ||
# @param regex [Regexp] a regex used for matching versions in content | ||
# @return [Hash] | ||
sig { | ||
params( | ||
url: String, | ||
regex: T.nilable(Regexp), | ||
_unused: T.nilable(T::Hash[Symbol, T.untyped]), | ||
block: T.nilable(Proc), | ||
).returns(T::Hash[Symbol, T.untyped]) | ||
} | ||
def self.find_versions(url:, regex: nil, **_unused, &block) | ||
match_data = { matches: {}, regex: regex || GithubRelease::DEFAULT_REGEX } | ||
match = url.sub(/\.git$/i, "").match(URL_MATCH_REGEX) | ||
|
||
return match_data if match.blank? | ||
|
||
releases = GitHub::API.open_rest("https://api.github.com/repos/#{match[:username]}/#{match[:repository]}/releases") | ||
|
||
GithubRelease.versions_from_content(releases, regex || DEFAULT_REGEX, &block).each do |match_text| | ||
match_data[:matches][match_text] = Version.new(match_text) | ||
end | ||
|
||
match_data | ||
end | ||
end | ||
end | ||
end | ||
end |