brew livecheck

brew livecheck 命令通过检查上游来查找公式或 cask 软件的最新版本。Livecheck 具有 策略,可从各种来源(如 Git 存储库、网站等)识别版本。

行为

当 livecheck 未收到有关如何检查上游版本的说明时,它默认执行以下操作

  1. 对于公式:按该顺序收集 stableheadhomepage URL(资源仅使用其 url)。对于 cask:按该顺序收集 urlhomepage URL。
  2. 确定是否有任何策略适用于第一个 URL。如果没有,请尝试下一个 URL。
  3. 如果可以应用策略,请使用它来检查新版本。
  4. 返回最新版本(或在任何可用 URL 中找不到版本时的错误)。

有时有必要覆盖此默认行为以创建有效检查。如果某个来源未提供最新版本,我们需要检查其他来源。如果 livecheck 未正确匹配版本文本,我们需要提供适当的正则表达式或 strategy 块。

可以通过向公式/cask/资源添加 livecheck 块来实现此目的。有关可用方法的更多信息,请参阅 Livecheck 类文档

创建检查

  1. 使用调试输出了解情况brew livecheck --debug <formula>|<cask> 提供有关 livecheck 尝试的 URL、适用的任何策略、匹配的版本等信息。

  2. 研究可用的来源以选择一个 URL。尝试从 stable/url 中移除文件名,看看它是否提供目录列表页面。如果不起作用,请尝试查找链接到该文件的页面(例如下载页面)。如果无法在网站上找到最新版本,请尝试检查公式/cask 中的其他来源。必要时,在公式/cask 外部搜索其他来源。

  3. 必要时创建正则表达式。如果检查在没有正则表达式的情况下也能正常工作,并且没有正则表达式可以受益,通常可以省略它。有关创建正则表达式的更多信息,请参阅 正则表达式指南 部分。

一般准则

URL 指南

正则表达式指南

livecheck 块正则表达式将匹配限制为获取内容的子集,并在版本文本周围使用捕获组。

示例 livecheck

以下示例涵盖了你可能遇到的许多模式。这些示例旨在成为具有代表性的示例,并且可以轻松改编。

如有疑问,请从以下示例之一开始,而不是从随机公式/cask 中复制粘贴 livecheck 块。

文件名

当从 HTML 页面上的文件名匹配版本时,我们通常将匹配限制为 href 属性。 href=.*? 将匹配开始定界符("')以及文件名之前 URL 的任何部分。

livecheck do
  url "https://www.example.com/downloads/"
  regex(/href=.*?example[._-]v?(\d+(?:\.\d+)+)\.t/i)
end

我们有时会更明确地排除不需要的匹配。具有前导路径的 URL 可以使用 href=.*?/,而其他 URL 可以使用 href=["']?。例如,当页面还包含具有较长前缀(another-example-1.2.tar.gz)的不需要文件时,这是必需的。

版本目录

当检查目录列表页面时,有时文件会分隔到版本目录中(例如 1.2.3/)。在这种情况下,我们必须从目录名称中识别版本。

livecheck do
  url "https://www.example.com/releases/example/"
  regex(%r{href=["']?v?(\d+(?:\.\d+)+)/?["' >]}i)
end

Git 标签

stable URL 使用 Git 策略时,以下示例将仅匹配 1.2/v1.2 等标签。

livecheck do
  url :stable
  regex(/^v?(\d+(?:\.\d+)+)$/i)
end

如果标签包含软件名称作为前缀(例如 example-1.2.3),则可以轻松地相应地修改正则表达式:/^example[._-]v?(\d+(?:\.\d+)+)$/i

引用的配方/cask

配方/cask 可以通过使用 formulacask 来使用与其他配方/cask 相同的检查。

livecheck do
  formula "another-formula"
end

引用的配方/cask 应位于同一 tap 中,因为如果用户尚未点击引用自其他 tap 的配方/cask,则会生成错误。

strategy

如果需要处理上游版本格式以匹配配方/cask 格式,则可以使用 strategy 块来代替 regex

PageMatch strategy

这是一个基本示例,从页面中提取简单版本

livecheck do
  url "https://example.org/my-app/download"
  regex(%r{href=.*?/MyApp-(\d+(?:\.\d+)*)\.zip}i)
  strategy :page_match
end

可以通过指定块来处理更复杂的版本。

livecheck do
  url "https://example.org/my-app/download"
  regex(%r{href=.*?/(\d+)/MyApp-(\d+(?:\.\d+)*)\.zip}i)
  strategy :page_match do |page, regex|
    match = page.match(regex)
    next if match.blank?

    "#{match[2]},#{match[1]}"
  end
end

在下面的示例中,我们正在扫描主页内容以查找类似于 2020-01-01 的日期格式并将其转换为 20200101

livecheck do
  url :homepage
  strategy :page_match do |page|
    page.scan(/href=.*?example[._-]v?(\d{4}-\d{2}-\d{2})\.t/i)
        .map { |match| match&.first&.gsub(/\D/, "") }
  end
end

此处看到的 PageMatch strategy 块样式也适用于任何在内部使用 PageMatch 的特定于站点的策略。

HeaderMatch strategy

用于 HeaderMatchstrategy 块将尝试从文件名(在 Content-Disposition 头中)和最终 URL(在 Location 头中)解析版本。如果不起作用,则可以指定 regex

livecheck do
  url "https://example.org/my-app/download/latest"
  regex(/MyApp-(\d+(?:\.\d+)*)\.zip/i)
  strategy :header_match
end

如果版本取决于多个头字段,则可以指定块。

livecheck do
  url "https://example.org/my-app/download/latest"
  strategy :header_match do |headers|
    v = headers["content-disposition"][/MyApp-(\d+(?:\.\d+)*)\.zip/i, 1]
    id = headers["location"][%r{/(\d+)/download$}i, 1]
    next if v.blank? || id.blank?

    "#{v},#{id}"
  end
end

Git strategy

用于 Gitstrategy 块有点不同,因为该块接收标签字符串数组而不是页面内容字符串。类似于 PageMatch 示例,这会将类似于 2020-01-01 的日期格式的标签转换为 20200101

livecheck do
  url :stable
  strategy :git do |tags|
    tags.map { |tag| tag[/^(\d{4}-\d{2}-\d{2})$/i, 1]&.gsub(/\D/, "") }.compact
  end
end

GithubLatest strategy

用于 GithubLateststrategy 块接收来自 GitHub API 的解析 JSON 数据,用于存储库的“最新”版本,以及正则表达式。当在 livecheck 块中未提供正则表达式时,策略的默认正则表达式将传递到 strategy 块中。

默认情况下,此策略匹配版本文本中的发行版标签或标题,但可以使用 strategy 块来检查发行版 JSON 中的任何字段。以下 strategy 块中的逻辑类似于默认行为,但仅检查发行版标签,以进行演示

livecheck do
  url :stable
  regex(/^example[._-]v?(\d+(?:\.\d+)+)$/i)
  strategy :github_latest do |json, regex|
    match = json["tag_name"]&.match(regex)
    next if match.blank?

    match[1]
  end
end

您可以在相关的 GitHub REST API 文档 中找到有关此 API 端点响应 JSON 的更多信息。

GithubReleases strategy

针对 GithubReleasesstrategy 块接收来自 GitHub API 的已解析 JSON 数据,其中包含存储库的最新发行版以及正则表达式。如果在 livecheck 块中未提供正则表达式,则会将策略的默认正则表达式传递到 strategy 块中。

默认情况下,此策略匹配每个发行版标签或标题中的版本文本,但可以使用 strategy 块来检查发行版 JSON 中的任何字段。以下 strategy 块中的逻辑类似于默认行为,但仅检查发行版标签,以进行演示

livecheck do
  url :stable
  regex(/^example[._-]v?(\d+(?:\.\d+)+)$/i)
  strategy :github_releases do |json, regex|
    json.map do |release|
      next if release["draft"] || release["prerelease"]

      match = release["tag_name"]&.match(regex)
      next if match.blank?

      match[1]
    end
  end
end

您可以在相关的 GitHub REST API 文档 中找到有关此 API 端点响应 JSON 的更多信息。

Crate strategy

针对 Cratestrategy 块接收来自注册表 API 的 versions 端点的已解析 JSON 数据以及提供的或默认的策略正则表达式。默认情况下,该策略使用以下逻辑,因此此 strategy 块可能是修改后方法的一个良好起点

livecheck do
  url :stable
  strategy :crate do |json, regex|
    json["versions"]&.map do |version|
      next if version["yanked"]
      next unless (match = version["num"]&.match(regex))

      match[1]
    end
  end
end

ElectronBuilder strategy

针对 ElectronBuilderstrategy 块获取 URL 中的内容,并将其解析为 YAML 格式的 electron-builder appcast。它用于使用 Electron 框架构建的 macOS 应用程序的 cask。

livecheck do
  url "https://example.org/my-app/latest-mac.yml"
  strategy :electron_builder
end

Json strategy

strategy 块用于 Json 接收已解析的 JSON 数据,如果提供,则接收正则表达式。例如,如果我们有一个包含一个对象数组的对象,其中包含一个 version 字符串,我们可以仅选择与正则表达式匹配的成员,并隔离相关的版本文本,如下所示

livecheck do
  url "https://www.example.com/example.json"
  regex(/^v?(\d+(?:\.\d+)+)$/i)
  strategy :json do |json, regex|
    json["versions"].select { |item| item["version"]&.match?(regex) }
                    .map { |item| item["version"][regex, 1] }
  end
end

Sparkle strategy

strategy 块用于 Sparkle 接收一个 item,该 item 具有 versionshort_versionnice_versionurlchanneltitle 的方法。它期望一个 XML feed 的 URL,该 URL 向使用 Sparkle 框架进行自我更新的 macOS 应用程序提供发行信息。此 URL 可以在应用程序包中找到,作为 Contents/Info.plist 中的 SUFeedURL 属性,或使用 find-appcast 脚本。使用以下命令运行它

"$(brew --repository homebrew/cask)/developer/bin/find-appcast" '/path/to/application.app'

Sparkle 策略的默认模式是从 sparkle:shortVersionStringsparkle:version 生成 "#{item.short_version},#{item.version}",如果两者都已设置。在下面的示例中,url 还包括一个下载 ID,这是必需的

livecheck do
  url "https://www.example.com/example.xml"
  strategy :sparkle do |item|
    "#{item.short_version},#{item.version}:#{item.url[%r{/(\d+)/[^/]+\.zip}i, 1]}"
  end
end

仅使用一个,请指定 &:version&:short_version&:nice_version

livecheck do
  url "https://www.example.com/example.xml"
  strategy :sparkle, &:short_version
end

Xml strategy

用于 Xmlstrategy 块接收一个 REXML::Document 对象,如果提供,则接收一个正则表达式。例如,如果 XML 包含一个 versions 元素,其中嵌套了 version 元素,并且它们的内部文本包含版本字符串,我们可以使用正则表达式将其提取出来,如下所示

livecheck do
  url "https://www.example.com/example.xml"
  regex(/v?(\d+(?:\.\d+)+)/i)
  strategy :xml do |xml, regex|
    xml.get_elements("versions//version").map { |item| item.text[regex, 1] }
  end
end

有关如何使用 REXML::Document 对象的更多信息,请参阅 REXML::DocumentREXML::Element 文档。

Yaml strategy

用于 Yamlstrategy 块接收已解析的 YAML 数据,如果提供,则接收一个正则表达式。借用 Json 示例,如果我们有一个包含一个对象数组的对象,其中包含一个 version 字符串,我们可以仅选择与正则表达式匹配的成员,并隔离相关的版本文本,如下所示

livecheck do
  url "https://www.example.com/example.yaml"
  regex(/^v?(\d+(?:\.\d+)+)$/i)
  strategy :yaml do |yaml, regex|
    yaml["versions"].select { |item| item["version"]&.match?(regex) }
                    .map { |item| item["version"][regex, 1] }
  end
end

ExtractPlist strategy

如果在线上无法找到任何方法来检查 macOS 软件包的哪个版本是当前版本,作为最后手段,:extract_plist 策略将让 brew livecheck 下载工件并从包含的 .plist 文件中检索其版本字符串。

livecheck do
  url :url
  strategy :extract_plist
end

用于 ExtractPliststrategy 块接收一个哈希,其中包含每个找到的包标识符的键和具有每个 versionshort_version 的方法的 item

livecheck do
  url :url
  strategy :extract_plist do |items|
    items["com.example.MyApp"].short_version
  end
end

跳过

出于多种原因(已弃用、已禁用等),Livecheck 会自动跳过某些公式/桶。但是,在极少数情况下,我们需要使用 livecheck 块进行手动跳过。 skip 方法采用一个字符串,其中包含跳过的非常简短的原因。

livecheck do
  skip "No version information available"
end
Fork me on GitHub