Module: Homebrew::Livecheck::Strategy Private

Extended by:
Utils::Curl
Defined in:
livecheck/strategy.rb,
livecheck/strategy/git.rb,
livecheck/strategy/gnu.rb,
livecheck/strategy/npm.rb,
livecheck/strategy/xml.rb,
livecheck/strategy/cpan.rb,
livecheck/strategy/json.rb,
livecheck/strategy/pypi.rb,
livecheck/strategy/xorg.rb,
livecheck/strategy/yaml.rb,
livecheck/strategy/crate.rb,
livecheck/strategy/gnome.rb,
livecheck/strategy/apache.rb,
livecheck/strategy/hackage.rb,
livecheck/strategy/sparkle.rb,
livecheck/strategy/bitbucket.rb,
livecheck/strategy/launchpad.rb,
livecheck/strategy/page_match.rb,
livecheck/strategy/sourceforge.rb,
livecheck/strategy/header_match.rb,
livecheck/strategy/extract_plist.rb,
livecheck/strategy/github_latest.rb,
livecheck/strategy/github_releases.rb,
livecheck/strategy/electron_builder.rb

Overview

This module is part of a private API. This module may only be used in the Homebrew/brew repository. Third parties should avoid using this module if possible, as it may be removed or changed without warning.

The Livecheck::Strategy module contains the various strategies as well as some general-purpose methods for working with them. Within the context of the brew livecheck command, strategies are established procedures for finding new software versions at a given source.

Defined Under Namespace

Classes: Apache, Bitbucket, Cpan, Crate, ElectronBuilder, ExtractPlist, Git, GithubLatest, GithubReleases, Gnome, Gnu, Hackage, HeaderMatch, Json, Launchpad, Npm, PageMatch, Pypi, Sourceforge, Sparkle, Xml, Xorg, Yaml

Constant Summary collapse

DEFAULT_PRIORITY =

This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.

Homebrew::Livecheck::Strategy priorities informally range from 1 to 10, where 10 is the highest priority. 5 is the default priority because it's roughly in the middle of this range. Strategies with a priority of 0 (or lower) are ignored.

5
CURL_CONNECT_TIMEOUT =

This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.

cURL's default --connect-timeout value can be up to two minutes, so we need to use a more reasonable duration (in seconds) to avoid a lengthy wait when a connection can't be established.

10
CURL_MAX_TIME =

This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.

cURL does not set a default --max-time value, so we provide a value to ensure cURL will time out in a reasonable amount of time.

CURL_CONNECT_TIMEOUT + 5
CURL_PROCESS_TIMEOUT =

This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.

The curl process will sometimes hang indefinitely (despite setting the --max-time argument) and it needs to be quit for livecheck to continue. This value is used to set the timeout argument on Utils::Curl method calls in Homebrew::Livecheck::Strategy.

CURL_MAX_TIME + 5
MAX_REDIRECTIONS =

This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.

The maximum number of redirections that curl should allow.

5
MAX_PARSE_ITERATIONS =

This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.

This value is passed to #parse_curl_output to ensure that the limit for the number of responses it will parse corresponds to the maximum number of responses in this context. The + 1 here accounts for the situation where there are exactly MAX_REDIRECTIONS number of redirections, followed by a final 200 OK response.

MAX_REDIRECTIONS + 1
DEFAULT_CURL_ARGS =

This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.

Baseline curl arguments used in Homebrew::Livecheck::Strategy methods.

[
  # Follow redirections to handle mirrors, relocations, etc.
  "--location",
  "--max-redirs", MAX_REDIRECTIONS.to_s,
  # Avoid progress bar text, so we can reliably identify `curl` error
  # messages in output
  "--silent"
].freeze
PAGE_CONTENT_CURL_ARGS =

This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.

curl arguments used in Strategy#page_content method.

([
  "--compressed",
  # Return an error when the HTTP response code is 400 or greater but
  # continue to return body content
  "--fail-with-body",
  # Include HTTP response headers in output, so we can identify the
  # final URL after any redirections
  "--include",
] + DEFAULT_CURL_ARGS).freeze
DEFAULT_CURL_OPTIONS =

This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.

Baseline curl options used in Homebrew::Livecheck::Strategy methods.

{
  print_stdout:    false,
  print_stderr:    false,
  debug:           false,
  verbose:         false,
  timeout:         CURL_PROCESS_TIMEOUT,
  connect_timeout: CURL_CONNECT_TIMEOUT,
  max_time:        CURL_MAX_TIME,
  retries:         0,
}.freeze
TARBALL_EXTENSION_REGEX =

This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.

A regex used to identify a tarball extension at the end of a string.

/
  \.t
  (?:ar(?:\.(?:bz2|gz|lz|lzma|lzo|xz|Z|zst))?|
  b2|bz2?|z2|az|gz|lz|lzma|xz|Z|aZ|zst)
  $
/ix
INVALID_BLOCK_RETURN_VALUE_MSG =

This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.

An error message to use when a strategy block returns a value of an inappropriate type.

"Return value of a strategy block must be a string or array of strings."

Class Method Summary collapse

Methods included from Utils::Curl

clear_path_cache, curl, curl_args, curl_check_http_content, curl_download, curl_executable, curl_headers, curl_http_content_headers_and_checksum, curl_output, curl_path, curl_response_follow_redirections, curl_response_last_location, curl_supports_fail_with_body?, curl_supports_tls13?, curl_with_workarounds, http_status_ok?, parse_curl_output, url_protected_by_cloudflare?, url_protected_by_incapsula?

Methods included from SystemCommand::Mixin

#system_command, #system_command!

Methods included from Kernel

#disk_usage_readable, #ensure_executable!, #ensure_formula_installed!, #exec_browser, #exec_editor, #ignore_interrupts, #interactive_shell, #number_readable, #odebug, #odeprecated, #odie, #odisabled, #ofail, #oh1, #oh1_title, #ohai, #ohai_title, #onoe, #opoo, #paths, #pretty_duration, #pretty_installed, #pretty_outdated, #pretty_uninstalled, #quiet_system, #redact_secrets, #redirect_stdout, #require?, #safe_system, #tap_and_name_comparison, #truncate_text_to_approximate_size, #which, #which_all, #which_editor, #with_custom_locale, #with_env, #with_homebrew_path

Class Method Details

.from_symbol(symbol) ⇒ T.untyped

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns the strategy that corresponds to the provided Symbol (or nil if there is no matching strategy).

Parameters:

  • symbol (Symbol, nil)

    the strategy name in snake case as a Symbol (e.g. :page_match)

Returns:

  • (T.untyped)


121
122
123
# File 'livecheck/strategy.rb', line 121

def from_symbol(symbol)
  strategies[symbol] if symbol.present?
end

.from_url(url, livecheck_strategy: nil, url_provided: false, regex_provided: false, block_provided: false) ⇒ Array<T.untyped>

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns an array of strategies that apply to the provided URL.

Parameters:

  • url (String)

    the URL to check for matching strategies

  • livecheck_strategy (Symbol, nil) (defaults to: nil)

    a strategy symbol from the livecheck block

  • url_provided (Boolean) (defaults to: false)

    whether a url is provided in the livecheck block

  • regex_provided (Boolean) (defaults to: false)

    whether a regex is provided in the livecheck block

  • block_provided (Boolean) (defaults to: false)

    whether a strategy block is provided in the livecheck block

Returns:



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'livecheck/strategy.rb', line 146

def from_url(url, livecheck_strategy: nil, url_provided: false, regex_provided: false, block_provided: false)
  usable_strategies = strategies.select do |strategy_symbol, strategy|
    if strategy == PageMatch
      # Only treat the strategy as usable if the `livecheck` block
      # contains a regex and/or `strategy` block
      next if !regex_provided && !block_provided
    elsif [Json, Xml, Yaml].include?(strategy)
      # Only treat the strategy as usable if the `livecheck` block
      # specifies the strategy and contains a `strategy` block
      next if (livecheck_strategy != strategy_symbol) || !block_provided
    elsif strategy.const_defined?(:PRIORITY) &&
          !strategy.const_get(:PRIORITY).positive? &&
          livecheck_strategy != strategy_symbol
      # Ignore strategies with a priority of 0 or lower, unless the
      # strategy is specified in the `livecheck` block
      next
    end

    strategy.respond_to?(:match?) && strategy.match?(url)
  end.values

  # Sort usable strategies in descending order by priority, using the
  # DEFAULT_PRIORITY when a strategy doesn't contain a PRIORITY constant
  usable_strategies.sort_by do |strategy|
    (strategy.const_defined?(:PRIORITY) ? -strategy.const_get(:PRIORITY) : -DEFAULT_PRIORITY)
  end
end

.handle_block_return(value) ⇒ Array<String>

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Handles the return value from a strategy block in a livecheck block.

Parameters:

  • value (T.untyped)

    [] the return value from a strategy block

Returns:



251
252
253
254
255
256
257
258
259
260
261
262
# File 'livecheck/strategy.rb', line 251

def self.handle_block_return(value)
  case value
  when String
    [value]
  when Array
    value.compact.uniq
  when nil
    []
  else
    raise TypeError, INVALID_BLOCK_RETURN_VALUE_MSG
  end
end

.page_content(url, homebrew_curl: false) ⇒ Hash{Symbol => T.untyped}

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Fetches the content at the URL and returns a hash containing the content and, if there are any redirections, the final URL. If curl encounters an error, the hash will contain a :messages array with the error message instead.

Parameters:

  • url (String)

    the URL of the content to check

  • homebrew_curl (Boolean) (defaults to: false)

    whether to use brewed curl with the URL

Returns:



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'livecheck/strategy.rb', line 216

def self.page_content(url, homebrew_curl: false)
  stderr = T.let(nil, T.nilable(String))
  [:default, :browser].each do |user_agent|
    stdout, stderr, status = curl_output(
      *PAGE_CONTENT_CURL_ARGS, url,
      **DEFAULT_CURL_OPTIONS,
      use_homebrew_curl: homebrew_curl || !curl_supports_fail_with_body?,
      user_agent:
    )
    next unless status.success?

    # stdout contains the header information followed by the page content.
    # We use #scrub here to avoid "invalid byte sequence in UTF-8" errors.
    output = stdout.scrub

    # Separate the head(s)/body and identify the final URL (after any
    # redirections)
    parsed_output = parse_curl_output(output, max_iterations: MAX_PARSE_ITERATIONS)
    final_url = curl_response_last_location(parsed_output[:responses], absolutize: true, base_url: url)

    data = { content: parsed_output[:body] }
    data[:final_url] = final_url if final_url.present? && final_url != url
    return data
  end

  error_msgs = stderr&.scan(/^curl:.+$/)
  { messages: error_msgs.presence || ["cURL failed without a detectable error"] }
end

.page_headers(url, homebrew_curl: false) ⇒ Array<Hash{String => String}>

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Collects HTTP response headers, starting with the provided URL. Redirections will be followed and all the response headers are collected into an array of hashes.

Parameters:

  • url (String)

    the URL to fetch

  • homebrew_curl (Boolean) (defaults to: false)

    whether to use brewed curl with the URL

Returns:



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'livecheck/strategy.rb', line 182

def self.page_headers(url, homebrew_curl: false)
  headers = []

  [:default, :browser].each do |user_agent|
    begin
      parsed_output = curl_headers(
        "--max-redirs",
        MAX_REDIRECTIONS.to_s,
        url,
        wanted_headers:    ["location", "content-disposition"],
        use_homebrew_curl: homebrew_curl,
        user_agent:,
        **DEFAULT_CURL_OPTIONS,
      )
    rescue ErrorDuringExecution
      next
    end

    parsed_output[:responses].each { |response| headers << response[:headers] }
    break if headers.present?
  end

  headers
end