Merge pull request #8578 from SeekingMeaning/livecheck/casks

livecheck: add support for casks
This commit is contained in:
Sam Ford 2020-12-12 18:20:17 -05:00 committed by GitHub
commit baac12b0bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 258 additions and 122 deletions

View File

@ -3,6 +3,7 @@
require "locale" require "locale"
require "lazy_object" require "lazy_object"
require "livecheck"
require "cask/artifact" require "cask/artifact"
@ -81,6 +82,8 @@ module Cask
:version, :version,
:appdir, :appdir,
:discontinued?, :discontinued?,
:livecheck,
:livecheckable?,
*ORDINARY_ARTIFACT_CLASSES.map(&:dsl_key), *ORDINARY_ARTIFACT_CLASSES.map(&:dsl_key),
*ACTIVATABLE_ARTIFACT_CLASSES.map(&:dsl_key), *ACTIVATABLE_ARTIFACT_CLASSES.map(&:dsl_key),
*ARTIFACT_BLOCK_CLASSES.flat_map { |klass| [klass.dsl_key, klass.uninstall_dsl_key] }, *ARTIFACT_BLOCK_CLASSES.flat_map { |klass| [klass.dsl_key, klass.uninstall_dsl_key] },
@ -273,6 +276,20 @@ module Cask
set_unique_stanza(:auto_updates, auto_updates.nil?) { auto_updates } set_unique_stanza(:auto_updates, auto_updates.nil?) { auto_updates }
end end
def livecheck(&block)
@livecheck ||= Livecheck.new(self)
return @livecheck unless block
raise CaskInvalidError.new(cask, "'livecheck' stanza may only appear once.") if @livecheckable
@livecheckable = true
@livecheck.instance_eval(&block)
end
def livecheckable?
@livecheckable == true
end
ORDINARY_ARTIFACT_CLASSES.each do |klass| ORDINARY_ARTIFACT_CLASSES.each do |klass|
define_method(klass.dsl_key) do |*args| define_method(klass.dsl_key) do |*args|
if [*artifacts.map(&:class), klass].include?(Artifact::StageOnly) && if [*artifacts.map(&:class), klass].include?(Artifact::StageOnly) &&

View File

@ -20,29 +20,35 @@ module Homebrew
def livecheck_args def livecheck_args
Homebrew::CLI::Parser.new do Homebrew::CLI::Parser.new do
usage_banner <<~EOS usage_banner <<~EOS
`livecheck` [<formulae>] `livecheck` [<formulae>|<casks>]
Check for newer versions of formulae from upstream. Check for newer versions of formulae and/or casks from upstream.
If no formula argument is passed, the list of formulae to check is taken from `HOMEBREW_LIVECHECK_WATCHLIST` If no formula or cask argument is passed, the list of formulae and
or `~/.brew_livecheck_watchlist`. casks to check is taken from `HOMEBREW_LIVECHECK_WATCHLIST` or
`~/.brew_livecheck_watchlist`.
EOS EOS
switch "--full-name", switch "--full-name",
description: "Print formulae with fully-qualified names." description: "Print formulae/casks with fully-qualified names."
flag "--tap=", flag "--tap=",
description: "Check formulae within the given tap, specified as <user>`/`<repo>." description: "Check formulae/casks within the given tap, specified as <user>`/`<repo>."
switch "--all", switch "--all",
description: "Check all available formulae." description: "Check all available formulae/casks."
switch "--installed", switch "--installed",
description: "Check formulae that are currently installed." description: "Check formulae/casks that are currently installed."
switch "--newer-only", switch "--newer-only",
description: "Show the latest version only if it's newer than the formula." description: "Show the latest version only if it's newer than the formula/cask."
switch "--json", switch "--json",
description: "Output information in JSON format." description: "Output information in JSON format."
switch "-q", "--quiet", switch "-q", "--quiet",
description: "Suppress warnings, don't print a progress bar for JSON output." description: "Suppress warnings, don't print a progress bar for JSON output."
switch "--formula", "--formulae",
description: "Only check formulae."
switch "--cask", "--casks",
description: "Only check casks."
conflicts "--debug", "--json" conflicts "--debug", "--json"
conflicts "--tap=", "--all", "--installed" conflicts "--tap=", "--all", "--installed"
conflicts "--cask", "--formula"
end end
end end
@ -54,28 +60,47 @@ module Homebrew
puts ENV["HOMEBREW_LIVECHECK_WATCHLIST"] if ENV["HOMEBREW_LIVECHECK_WATCHLIST"].present? puts ENV["HOMEBREW_LIVECHECK_WATCHLIST"] if ENV["HOMEBREW_LIVECHECK_WATCHLIST"].present?
end end
formulae_to_check = if args.tap formulae_and_casks_to_check = if args.tap
Tap.fetch(args.tap).formula_names.map { |name| Formula[name] } tap = Tap.fetch(args.tap)
formulae = args.cask? ? [] : tap.formula_files.map { |path| Formulary.factory(path) }
casks = args.formula? ? [] : tap.cask_files.map { |path| Cask::CaskLoader.load(path) }
formulae + casks
elsif args.installed? elsif args.installed?
Formula.installed formulae = args.cask? ? [] : Formula.installed
casks = args.formula? ? [] : Cask::Caskroom.casks
formulae + casks
elsif args.all? elsif args.all?
Formula formulae = args.cask? ? [] : Formula.to_a
elsif (formulae_args = args.named.to_formulae) && formulae_args.present? casks = args.formula? ? [] : Cask::Cask.to_a
formulae_args formulae + casks
elsif args.named.present?
if args.formula?
args.named.to_formulae
elsif args.cask?
args.named.to_casks
else
args.named.to_formulae_and_casks
end
elsif File.exist?(WATCHLIST_PATH) elsif File.exist?(WATCHLIST_PATH)
begin begin
Pathname.new(WATCHLIST_PATH).read.lines.map do |line| names = Pathname.new(WATCHLIST_PATH).read.lines
next if line.start_with?("#") .reject { |line| line.start_with?("#") || line.blank? }
.map(&:strip)
Formula[line.strip] named_args = T.unsafe(CLI::NamedArgs).new(*names)
end.compact named_args.to_formulae_and_casks.reject do |formula_or_cask|
(args.formula? && !formula_or_cask.is_a?(Formula)) ||
(args.cask? && !formula_or_cask.is_a?(Cask::Cask))
end
rescue Errno::ENOENT => e rescue Errno::ENOENT => e
onoe e onoe e
end end
end.sort_by do |formula_or_cask|
formula_or_cask.respond_to?(:token) ? formula_or_cask.token : formula_or_cask.name
end end
raise UsageError, "No formulae to check." if formulae_to_check.blank? raise UsageError, "No formulae or casks to check." if formulae_and_casks_to_check.blank?
Livecheck.livecheck_formulae(formulae_to_check, args) Livecheck.run_checks(formulae_and_casks_to_check, args)
end end
end end

View File

@ -1,20 +1,20 @@
# typed: true # typed: true
# frozen_string_literal: true # frozen_string_literal: true
# The {Livecheck} class implements the DSL methods used in a formula's # The {Livecheck} class implements the DSL methods used in a formula's or cask's
# `livecheck` block and stores related instance variables. Most of these methods # `livecheck` block and stores related instance variables. Most of these methods
# also return the related instance variable when no argument is provided. # also return the related instance variable when no argument is provided.
# #
# This information is used by the `brew livecheck` command to control its # This information is used by the `brew livecheck` command to control its
# behavior. # behavior.
class Livecheck class Livecheck
# A very brief description of why the formula is skipped (e.g. `No longer # A very brief description of why the formula/cask is skipped (e.g. `No longer
# developed or maintained`). # developed or maintained`).
# @return [String, nil] # @return [String, nil]
attr_reader :skip_msg attr_reader :skip_msg
def initialize(formula) def initialize(formula_or_cask)
@formula = formula @formula_or_cask = formula_or_cask
@regex = nil @regex = nil
@skip = false @skip = false
@skip_msg = nil @skip_msg = nil
@ -40,10 +40,10 @@ class Livecheck
# Sets the `@skip` instance variable to `true` and sets the `@skip_msg` # Sets the `@skip` instance variable to `true` and sets the `@skip_msg`
# instance variable if a `String` is provided. `@skip` is used to indicate # instance variable if a `String` is provided. `@skip` is used to indicate
# that the formula should be skipped and the `skip_msg` very briefly describes # that the formula/cask should be skipped and the `skip_msg` very briefly
# why the formula is skipped (e.g. "No longer developed or maintained"). # describes why it is skipped (e.g. "No longer developed or maintained").
# #
# @param skip_msg [String] string describing why the formula is skipped # @param skip_msg [String] string describing why the formula/cask is skipped
# @return [Boolean] # @return [Boolean]
def skip(skip_msg = nil) def skip(skip_msg = nil)
if skip_msg.is_a?(String) if skip_msg.is_a?(String)
@ -55,7 +55,7 @@ class Livecheck
@skip = true @skip = true
end end
# Should `livecheck` skip this formula? # Should `livecheck` skip this formula/cask?
def skip? def skip?
@skip @skip
end end
@ -81,18 +81,21 @@ class Livecheck
# Sets the `@url` instance variable to the provided argument or returns the # Sets the `@url` instance variable to the provided argument or returns the
# `@url` instance variable when no argument is provided. The argument can be # `@url` instance variable when no argument is provided. The argument can be
# a `String` (a URL) or a supported `Symbol` corresponding to a URL in the # a `String` (a URL) or a supported `Symbol` corresponding to a URL in the
# formula (e.g. `:stable`, `:homepage`, or `:head`). # formula/cask (e.g. `:stable`, `:homepage`, `:head`, `:cask_url`, `:appcast`).
#
# @param val [String, Symbol] URL to check for version information # @param val [String, Symbol] URL to check for version information
# @return [String, nil] # @return [String, nil]
def url(val = nil) def url(val = nil)
@url = case val @url = case val
when nil when nil
return @url return @url
when :appcast
@formula_or_cask.appcast.to_s
when :cask_url
@formula_or_cask.url.to_s
when :head, :stable when :head, :stable
@formula.send(val).url @formula_or_cask.send(val).url
when :homepage when :homepage
@formula.homepage @formula_or_cask.homepage
when String when String
val val
else else

View File

@ -41,18 +41,18 @@ module Homebrew
rc rc
].freeze ].freeze
# Executes the livecheck logic for each formula in the `formulae_to_check` array # Executes the livecheck logic for each formula/cask in the
# and prints the results. # `formulae_and_casks_to_check` array and prints the results.
# @return [nil] # @return [nil]
def livecheck_formulae(formulae_to_check, args) def run_checks(formulae_and_casks_to_check, args)
# Identify any non-homebrew/core taps in use for current formulae # Identify any non-homebrew/core taps in use for current formulae
non_core_taps = {} non_core_taps = {}
formulae_to_check.each do |f| formulae_and_casks_to_check.each do |formula_or_cask|
next if f.tap.blank? next if formula_or_cask.tap.blank?
next if f.tap.name == CoreTap.instance.name next if formula_or_cask.tap.name == CoreTap.instance.name
next if non_core_taps[f.tap.name] next if non_core_taps[formula_or_cask.tap.name]
non_core_taps[f.tap.name] = f.tap non_core_taps[formula_or_cask.tap.name] = formula_or_cask.tap
end end
non_core_taps = non_core_taps.sort.to_h non_core_taps = non_core_taps.sort.to_h
@ -73,10 +73,10 @@ module Homebrew
has_a_newer_upstream_version = false has_a_newer_upstream_version = false
if args.json? && !args.quiet? && $stderr.tty? if args.json? && !args.quiet? && $stderr.tty?
total_formulae = if formulae_to_check == Formula formulae_and_casks_total = if formulae_and_casks_to_check == Formula
formulae_to_check.count formulae_and_casks_to_check.count
else else
formulae_to_check.length formulae_and_casks_to_check.length
end end
Tty.with($stderr) do |stderr| Tty.with($stderr) do |stderr|
@ -84,7 +84,7 @@ module Homebrew
end end
progress = ProgressBar.create( progress = ProgressBar.create(
total: total_formulae, total: formulae_and_casks_total,
progress_mark: "#", progress_mark: "#",
remainder_mark: ".", remainder_mark: ".",
format: " %t: [%B] %c/%C ", format: " %t: [%B] %c/%C ",
@ -92,7 +92,10 @@ module Homebrew
) )
end end
formulae_checked = formulae_to_check.sort.map.with_index do |formula, i| formulae_checked = formulae_and_casks_to_check.map.with_index do |formula_or_cask, i|
formula = formula_or_cask if formula_or_cask.is_a?(Formula)
cask = formula_or_cask if formula_or_cask.is_a?(Cask::Cask)
if args.debug? && i.positive? if args.debug? && i.positive?
puts <<~EOS puts <<~EOS
@ -101,25 +104,29 @@ module Homebrew
EOS EOS
end end
skip_result = skip_conditions(formula, args: args) skip_result = skip_conditions(formula_or_cask, args: args)
next skip_result if skip_result != false next skip_result if skip_result != false
formula.head&.downloader&.shutup! formula&.head&.downloader&.shutup!
# Use the `stable` version for comparison except for installed # Use the `stable` version for comparison except for installed
# head-only formulae. A formula with `stable` and `head` that's # head-only formulae. A formula with `stable` and `head` that's
# installed using `--head` will still use the `stable` version for # installed using `--head` will still use the `stable` version for
# comparison. # comparison.
current = if formula.head_only? current = if formula
formula.any_installed_version.version.commit if formula.head_only?
formula.any_installed_version.version.commit
else
formula.stable.version
end
else else
formula.stable.version Version.new(formula_or_cask.version)
end end
latest = if formula.head_only? latest = if formula&.head_only?
formula.head.downloader.fetch_last_commit formula.head.downloader.fetch_last_commit
else else
version_info = latest_version(formula, args: args) version_info = latest_version(formula_or_cask, args: args)
version_info[:latest] if version_info.present? version_info[:latest] if version_info.present?
end end
@ -129,14 +136,14 @@ module Homebrew
next version_info if version_info.is_a?(Hash) && version_info[:status] && version_info[:messages] next version_info if version_info.is_a?(Hash) && version_info[:status] && version_info[:messages]
next status_hash(formula, "error", [no_versions_msg], args: args) next status_hash(formula_or_cask, "error", [no_versions_msg], args: args)
end end
if (m = latest.to_s.match(/(.*)-release$/)) && !current.to_s.match(/.*-release$/) if (m = latest.to_s.match(/(.*)-release$/)) && !current.to_s.match(/.*-release$/)
latest = Version.new(m[1]) latest = Version.new(m[1])
end end
is_outdated = if formula.head_only? is_outdated = if formula&.head_only?
# A HEAD-only formula is considered outdated if the latest upstream # A HEAD-only formula is considered outdated if the latest upstream
# commit hash is different than the installed version's commit hash # commit hash is different than the installed version's commit hash
(current != latest) (current != latest)
@ -144,21 +151,21 @@ module Homebrew
(current < latest) (current < latest)
end end
is_newer_than_upstream = formula.stable? && (current > latest) is_newer_than_upstream = (formula&.stable? || cask) && (current > latest)
info = { info = {}
formula: formula_name(formula, args: args), info[:formula] = formula_name(formula, args: args) if formula
version: { info[:cask] = cask_name(cask, args: args) if cask
current: current.to_s, info[:version] = {
latest: latest.to_s, current: current.to_s,
outdated: is_outdated, latest: latest.to_s,
newer_than_upstream: is_newer_than_upstream, outdated: is_outdated,
}, newer_than_upstream: is_newer_than_upstream,
meta: {
livecheckable: formula.livecheckable?,
},
} }
info[:meta][:head_only] = true if formula.head_only? info[:meta] = {
livecheckable: formula_or_cask.livecheckable?,
}
info[:meta][:head_only] = true if formula&.head_only?
info[:meta].merge!(version_info[:meta]) if version_info.present? && version_info.key?(:meta) info[:meta].merge!(version_info[:meta]) if version_info.present? && version_info.key?(:meta)
next if args.newer_only? && !info[:version][:outdated] next if args.newer_only? && !info[:version][:outdated]
@ -178,9 +185,9 @@ module Homebrew
if args.json? if args.json?
progress&.increment progress&.increment
status_hash(formula, "error", [e.to_s], args: args) status_hash(formula_or_cask, "error", [e.to_s], args: args)
elsif !args.quiet? elsif !args.quiet?
onoe "#{Tty.blue}#{formula_name(formula, args: args)}#{Tty.reset}: #{e}" onoe "#{Tty.blue}#{formula_or_cask_name(formula_or_cask, args: args)}#{Tty.reset}: #{e}"
nil nil
end end
end end
@ -201,6 +208,19 @@ module Homebrew
puts JSON.generate(formulae_checked.compact) puts JSON.generate(formulae_checked.compact)
end end
def formula_or_cask_name(formula_or_cask, args:)
case formula_or_cask
when Formula
formula_name(formula_or_cask, args: args)
when Cask::Cask
cask_name(formula_or_cask, args: args)
end
end
def cask_name(cask, args:)
args.full_name? ? cask.full_name : cask.token
end
# Returns the fully-qualified name of a formula if the `full_name` argument is # Returns the fully-qualified name of a formula if the `full_name` argument is
# provided; returns the name otherwise. # provided; returns the name otherwise.
# @return [String] # @return [String]
@ -208,18 +228,24 @@ module Homebrew
args.full_name? ? formula.full_name : formula.name args.full_name? ? formula.full_name : formula.name
end end
def status_hash(formula, status_str, messages = nil, args:) def status_hash(formula_or_cask, status_str, messages = nil, args:)
status_hash = { formula = formula_or_cask if formula_or_cask.is_a?(Formula)
formula: formula_name(formula, args: args), cask = formula_or_cask if formula_or_cask.is_a?(Cask::Cask)
status: status_str,
} status_hash = {}
if formula
status_hash[:formula] = formula_name(formula, args: args)
elsif cask
status_hash[:cask] = cask_name(formula_or_cask, args: args)
end
status_hash[:status] = status_str
status_hash[:messages] = messages if messages.is_a?(Array) status_hash[:messages] = messages if messages.is_a?(Array)
if args.verbose? if args.verbose?
status_hash[:meta] = { status_hash[:meta] = {
livecheckable: formula.livecheckable?, livecheckable: formula_or_cask.livecheckable?,
} }
status_hash[:meta][:head_only] = true if formula.head_only? status_hash[:meta][:head_only] = true if formula&.head_only?
end end
status_hash status_hash
@ -228,29 +254,31 @@ module Homebrew
# If a formula has to be skipped, it prints or returns a Hash contaning the reason # If a formula has to be skipped, it prints or returns a Hash contaning the reason
# for doing so; returns false otherwise. # for doing so; returns false otherwise.
# @return [Hash, nil, Boolean] # @return [Hash, nil, Boolean]
def skip_conditions(formula, args:) def skip_conditions(formula_or_cask, args:)
if formula.deprecated? && !formula.livecheckable? formula = formula_or_cask if formula_or_cask.is_a?(Formula)
if formula&.deprecated? && !formula.livecheckable?
return status_hash(formula, "deprecated", args: args) if args.json? return status_hash(formula, "deprecated", args: args) if args.json?
puts "#{Tty.red}#{formula_name(formula, args: args)}#{Tty.reset} : deprecated" unless args.quiet? puts "#{Tty.red}#{formula_name(formula, args: args)}#{Tty.reset} : deprecated" unless args.quiet?
return return
end end
if formula.disabled? && !formula.livecheckable? if formula&.disabled? && !formula.livecheckable?
return status_hash(formula, "disabled", args: args) if args.json? return status_hash(formula, "disabled", args: args) if args.json?
puts "#{Tty.red}#{formula_name(formula, args: args)}#{Tty.reset} : disabled" unless args.quiet? puts "#{Tty.red}#{formula_name(formula, args: args)}#{Tty.reset} : disabled" unless args.quiet?
return return
end end
if formula.versioned_formula? && !formula.livecheckable? if formula&.versioned_formula? && !formula.livecheckable?
return status_hash(formula, "versioned", args: args) if args.json? return status_hash(formula, "versioned", args: args) if args.json?
puts "#{Tty.red}#{formula_name(formula, args: args)}#{Tty.reset} : versioned" unless args.quiet? puts "#{Tty.red}#{formula_name(formula, args: args)}#{Tty.reset} : versioned" unless args.quiet?
return return
end end
if formula.head_only? && !formula.any_version_installed? if formula&.head_only? && !formula.any_version_installed?
head_only_msg = "HEAD only formula must be installed to be livecheckable" head_only_msg = "HEAD only formula must be installed to be livecheckable"
return status_hash(formula, "error", [head_only_msg], args: args) if args.json? return status_hash(formula, "error", [head_only_msg], args: args) if args.json?
@ -258,21 +286,21 @@ module Homebrew
return return
end end
is_gist = formula.stable&.url&.include?("gist.github.com") is_gist = formula&.stable&.url&.include?("gist.github.com")
if formula.livecheck.skip? || is_gist if formula_or_cask.livecheck.skip? || is_gist
skip_msg = if formula.livecheck.skip_msg.is_a?(String) && skip_msg = if formula_or_cask.livecheck.skip_msg.is_a?(String) &&
formula.livecheck.skip_msg.present? formula_or_cask.livecheck.skip_msg.present?
formula.livecheck.skip_msg.to_s formula_or_cask.livecheck.skip_msg.to_s
elsif is_gist elsif is_gist
"Stable URL is a GitHub Gist" "Stable URL is a GitHub Gist"
else else
"" ""
end end
return status_hash(formula, "skipped", (skip_msg.blank? ? nil : [skip_msg]), args: args) if args.json? return status_hash(formula_or_cask, "skipped", (skip_msg.blank? ? nil : [skip_msg]), args: args) if args.json?
unless args.quiet? unless args.quiet?
puts "#{Tty.red}#{formula_name(formula, args: args)}#{Tty.reset} : skipped" \ puts "#{Tty.red}#{formula_or_cask_name(formula_or_cask, args: args)}#{Tty.reset} : skipped" \
"#{" - #{skip_msg}" if skip_msg.present?}" "#{" - #{skip_msg}" if skip_msg.present?}"
end end
return return
@ -284,8 +312,8 @@ module Homebrew
# Formats and prints the livecheck result for a formula. # Formats and prints the livecheck result for a formula.
# @return [nil] # @return [nil]
def print_latest_version(info, args:) def print_latest_version(info, args:)
formula_s = "#{Tty.blue}#{info[:formula]}#{Tty.reset}" formula_or_cask_s = "#{Tty.blue}#{info[:formula] || info[:cask]}#{Tty.reset}"
formula_s += " (guessed)" if !info[:meta][:livecheckable] && args.verbose? formula_or_cask_s += " (guessed)" if !info[:meta][:livecheckable] && args.verbose?
current_s = if info[:version][:newer_than_upstream] current_s = if info[:version][:newer_than_upstream]
"#{Tty.red}#{info[:version][:current]}#{Tty.reset}" "#{Tty.red}#{info[:version][:current]}#{Tty.reset}"
@ -299,19 +327,27 @@ module Homebrew
info[:version][:latest] info[:version][:latest]
end end
puts "#{formula_s} : #{current_s} ==> #{latest_s}" puts "#{formula_or_cask_s} : #{current_s} ==> #{latest_s}"
end end
# Returns an Array containing the formula URLs that can be used by livecheck. # Returns an Array containing the formula/cask URLs that can be used by livecheck.
# @return [Array] # @return [Array]
def checkable_urls(formula) def checkable_urls(formula_or_cask)
urls = [] urls = []
urls << formula.head.url if formula.head
if formula.stable case formula_or_cask
urls << formula.stable.url when Formula
urls.concat(formula.stable.mirrors) urls << formula_or_cask.head.url if formula_or_cask.head
if formula_or_cask.stable
urls << formula_or_cask.stable.url
urls.concat(formula_or_cask.stable.mirrors)
end
urls << formula_or_cask.homepage if formula_or_cask.homepage
when Cask::Cask
urls << formula_or_cask.appcast.to_s if formula_or_cask.appcast
urls << formula_or_cask.url.to_s if formula_or_cask.url
urls << formula_or_cask.homepage if formula_or_cask.homepage
end end
urls << formula.homepage if formula.homepage
urls.compact urls.compact
end end
@ -357,20 +393,27 @@ module Homebrew
# Identifies the latest version of the formula and returns a Hash containing # Identifies the latest version of the formula and returns a Hash containing
# the version information. Returns nil if a latest version couldn't be found. # the version information. Returns nil if a latest version couldn't be found.
# @return [Hash, nil] # @return [Hash, nil]
def latest_version(formula, args:) def latest_version(formula_or_cask, args:)
has_livecheckable = formula.livecheckable? formula = formula_or_cask if formula_or_cask.is_a?(Formula)
livecheck = formula.livecheck cask = formula_or_cask if formula_or_cask.is_a?(Cask::Cask)
has_livecheckable = formula_or_cask.livecheckable?
livecheck = formula_or_cask.livecheck
livecheck_regex = livecheck.regex livecheck_regex = livecheck.regex
livecheck_strategy = livecheck.strategy livecheck_strategy = livecheck.strategy
livecheck_url = livecheck.url livecheck_url = livecheck.url
urls = [livecheck_url] if livecheck_url.present? urls = [livecheck_url] if livecheck_url.present?
urls ||= checkable_urls(formula) urls ||= checkable_urls(formula_or_cask)
if args.debug? if args.debug?
puts puts
puts "Formula: #{formula_name(formula, args: args)}" if formula
puts "Head only?: true" if formula.head_only? puts "Formula: #{formula_name(formula, args: args)}"
puts "Head only?: true" if formula.head_only?
elsif cask
puts "Cask: #{cask_name(formula_or_cask, args: args)}"
end
puts "Livecheckable?: #{has_livecheckable ? "Yes" : "No"}" puts "Livecheckable?: #{has_livecheckable ? "Yes" : "No"}"
end end

View File

@ -9,6 +9,7 @@ module RuboCop
[:version, :sha256], [:version, :sha256],
[:language], [:language],
[:url, :appcast, :name, :desc, :homepage], [:url, :appcast, :name, :desc, :homepage],
[:livecheck],
[ [
:auto_updates, :auto_updates,
:conflicts_with, :conflicts_with,

View File

@ -74,6 +74,23 @@ describe Homebrew::Livecheck do
end end
end end
let(:c) do
Cask::CaskLoader.load(+<<-RUBY)
cask "test" do
version "0.0.1,2"
url "https://brew.sh/test-0.0.1.tgz"
name "Test"
homepage "https://brew.sh"
livecheck do
url "https://formulae.brew.sh/api/formula/ruby.json"
regex(/"stable":"(\d+(?:\.\d+)+)"/i)
end
end
RUBY
end
let(:args) { double("livecheck_args", full_name?: false, json?: false, quiet?: false, verbose?: true) } let(:args) { double("livecheck_args", full_name?: false, json?: false, quiet?: false, verbose?: true) }
describe "::formula_name" do describe "::formula_name" do
@ -88,6 +105,18 @@ describe Homebrew::Livecheck do
end end
end end
describe "::cask_name" do
it "returns the token of the cask" do
expect(livecheck.cask_name(c, args: args)).to eq("test")
end
it "returns the full name of the cask" do
allow(args).to receive(:full_name?).and_return(true)
expect(livecheck.cask_name(c, args: args)).to eq("test")
end
end
describe "::status_hash" do describe "::status_hash" do
it "returns a hash containing the livecheck status" do it "returns a hash containing the livecheck status" do
expect(livecheck.status_hash(f, "error", ["Unable to get versions"], args: args)) expect(livecheck.status_hash(f, "error", ["Unable to get versions"], args: args))
@ -142,6 +171,10 @@ describe Homebrew::Livecheck do
it "returns false for a non-skippable formula" do it "returns false for a non-skippable formula" do
expect(livecheck.skip_conditions(f, args: args)).to eq(false) expect(livecheck.skip_conditions(f, args: args)).to eq(false)
end end
it "returns false for a non-skippable cask" do
expect(livecheck.skip_conditions(c, args: args)).to eq(false)
end
end end
describe "::checkable_urls" do describe "::checkable_urls" do
@ -150,6 +183,7 @@ describe Homebrew::Livecheck do
.to eq( .to eq(
["https://github.com/Homebrew/brew.git", "https://brew.sh/test-0.0.1.tgz", "https://brew.sh"], ["https://github.com/Homebrew/brew.git", "https://brew.sh/test-0.0.1.tgz", "https://brew.sh"],
) )
expect(livecheck.checkable_urls(c)).to eq(["https://brew.sh/test-0.0.1.tgz", "https://brew.sh"])
end end
end end

View File

@ -1044,27 +1044,32 @@ provided, check all kegs. Raises an error if run on uninstalled formulae.
* `--cached`: * `--cached`:
Print the cached linkage values stored in `HOMEBREW_CACHE`, set by a previous `brew linkage` run. Print the cached linkage values stored in `HOMEBREW_CACHE`, set by a previous `brew linkage` run.
### `livecheck` [*`formulae`*] ### `livecheck` [*`formulae`*|*`casks`*]
Check for newer versions of formulae from upstream. Check for newer versions of formulae and/or casks from upstream.
If no formula argument is passed, the list of formulae to check is taken from `HOMEBREW_LIVECHECK_WATCHLIST` If no formula or cask argument is passed, the list of formulae and
or `~/.brew_livecheck_watchlist`. casks to check is taken from `HOMEBREW_LIVECHECK_WATCHLIST` or
`~/.brew_livecheck_watchlist`.
* `--full-name`: * `--full-name`:
Print formulae with fully-qualified names. Print formulae/casks with fully-qualified names.
* `--tap`: * `--tap`:
Check formulae within the given tap, specified as *`user`*`/`*`repo`*. Check formulae/casks within the given tap, specified as *`user`*`/`*`repo`*.
* `--all`: * `--all`:
Check all available formulae. Check all available formulae/casks.
* `--installed`: * `--installed`:
Check formulae that are currently installed. Check formulae/casks that are currently installed.
* `--newer-only`: * `--newer-only`:
Show the latest version only if it's newer than the formula. Show the latest version only if it's newer than the formula/cask.
* `--json`: * `--json`:
Output information in JSON format. Output information in JSON format.
* `-q`, `--quiet`: * `-q`, `--quiet`:
Suppress warnings, don't print a progress bar for JSON output. Suppress warnings, don't print a progress bar for JSON output.
* `--formula`:
Only check formulae.
* `--cask`:
Only check casks.
### `man` [*`options`*] ### `man` [*`options`*]

View File

@ -1442,31 +1442,31 @@ For every library that a keg references, print its dylib path followed by the bi
\fB\-\-cached\fR \fB\-\-cached\fR
Print the cached linkage values stored in \fBHOMEBREW_CACHE\fR, set by a previous \fBbrew linkage\fR run\. Print the cached linkage values stored in \fBHOMEBREW_CACHE\fR, set by a previous \fBbrew linkage\fR run\.
. .
.SS "\fBlivecheck\fR [\fIformulae\fR]" .SS "\fBlivecheck\fR [\fIformulae\fR|\fIcasks\fR]"
Check for newer versions of formulae from upstream\. Check for newer versions of formulae and/or casks from upstream\.
. .
.P .P
If no formula argument is passed, the list of formulae to check is taken from \fBHOMEBREW_LIVECHECK_WATCHLIST\fR or \fB~/\.brew_livecheck_watchlist\fR\. If no formula or cask argument is passed, the list of formulae and casks to check is taken from \fBHOMEBREW_LIVECHECK_WATCHLIST\fR or \fB~/\.brew_livecheck_watchlist\fR\.
. .
.TP .TP
\fB\-\-full\-name\fR \fB\-\-full\-name\fR
Print formulae with fully\-qualified names\. Print formulae/casks with fully\-qualified names\.
. .
.TP .TP
\fB\-\-tap\fR \fB\-\-tap\fR
Check formulae within the given tap, specified as \fIuser\fR\fB/\fR\fIrepo\fR\. Check formulae/casks within the given tap, specified as \fIuser\fR\fB/\fR\fIrepo\fR\.
. .
.TP .TP
\fB\-\-all\fR \fB\-\-all\fR
Check all available formulae\. Check all available formulae/casks\.
. .
.TP .TP
\fB\-\-installed\fR \fB\-\-installed\fR
Check formulae that are currently installed\. Check formulae/casks that are currently installed\.
. .
.TP .TP
\fB\-\-newer\-only\fR \fB\-\-newer\-only\fR
Show the latest version only if it\'s newer than the formula\. Show the latest version only if it\'s newer than the formula/cask\.
. .
.TP .TP
\fB\-\-json\fR \fB\-\-json\fR
@ -1476,6 +1476,14 @@ Output information in JSON format\.
\fB\-q\fR, \fB\-\-quiet\fR \fB\-q\fR, \fB\-\-quiet\fR
Suppress warnings, don\'t print a progress bar for JSON output\. Suppress warnings, don\'t print a progress bar for JSON output\.
. .
.TP
\fB\-\-formula\fR
Only check formulae\.
.
.TP
\fB\-\-cask\fR
Only check casks\.
.
.SS "\fBman\fR [\fIoptions\fR]" .SS "\fBman\fR [\fIoptions\fR]"
Generate Homebrew\'s manpages\. Generate Homebrew\'s manpages\.
. .