Module: Homebrew::Livecheck::Strategy Private

Extended by:
T::Sig
Defined in:
livecheck/strategy.rb,
livecheck/strategy/git.rb,
livecheck/strategy/gnu.rb,
livecheck/strategy/npm.rb,
livecheck/strategy/cpan.rb,
livecheck/strategy/pypi.rb,
livecheck/strategy/xorg.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/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, ElectronBuilder, ExtractPlist, Git, GithubLatest, Gnome, Gnu, Hackage, HeaderMatch, Launchpad, Npm, PageMatch, Pypi, Sourceforge, Sparkle, Xorg

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
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",
].freeze
PAGE_HEADERS_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_headers method.

([
  # We only need the response head (not the body)
  "--head",
  # Some servers may not allow a HEAD request, so we use GET
  "--request", "GET",
  "--silent"
] + DEFAULT_CURL_ARGS).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",
  # 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
HTTP_HEAD_BODY_SEPARATOR =

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.

HTTP response head(s) and body are typically separated by a double CRLF (whereas HTTP header lines are separated by a single CRLF). In rare cases, this can also be a double newline (\n\n).

"\r\n\r\n"
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.freeze
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

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, nil)


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

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:

  • (Array<T.untyped>)


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

def from_url(url, livecheck_strategy: nil, url_provided: false, regex_provided: false, block_provided: false)
  usable_strategies = strategies.values.select do |strategy|
    if strategy == PageMatch
      # Only treat the `PageMatch` strategy as usable if a regex is
      # present in the `livecheck` block
      next if !regex_provided && !block_provided
    elsif strategy.const_defined?(:PRIORITY) &&
          !strategy::PRIORITY.positive? &&
          from_symbol(livecheck_strategy) != strategy
      # 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

  # 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::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:



260
261
262
263
264
265
266
267
268
269
270
271
# File 'livecheck/strategy.rb', line 260

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) ⇒ 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

Returns:



208
209
210
211
212
213
214
215
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
244
245
246
247
248
249
250
251
252
# File 'livecheck/strategy.rb', line 208

def self.page_content(url)
  original_url = url

  stderr = nil
  [:default, :browser].each do |user_agent|
    stdout, stderr, status = curl_with_workarounds(
      *PAGE_CONTENT_CURL_ARGS, url,
      **DEFAULT_CURL_OPTIONS,
      user_agent: 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)
    max_iterations = 5
    iterations = 0
    output = output.lstrip
    while output.match?(%r{\AHTTP/[\d.]+ \d+}) && output.include?(HTTP_HEAD_BODY_SEPARATOR)
      iterations += 1
      raise "Too many redirects (max = #{max_iterations})" if iterations > max_iterations

      head_text, _, output = output.partition(HTTP_HEAD_BODY_SEPARATOR)
      output = output.lstrip

      location = head_text[/^Location:\s*(.*)$/i, 1]
      next if location.blank?

      location.chomp!
      # Convert a relative redirect URL to an absolute URL
      location = URI.join(url, location) unless location.match?(PageMatch::URL_MATCH_REGEX)
      final_url = location
    end

    data = { content: output }
    data[:final_url] = final_url if final_url.present? && final_url != original_url
    return data
  end

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

.page_headers(url) ⇒ 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

Returns:



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'livecheck/strategy.rb', line 176

def self.page_headers(url)
  headers = []

  [:default, :browser].each do |user_agent|
    stdout, _, status = curl_with_workarounds(
      *PAGE_HEADERS_CURL_ARGS, url,
      **DEFAULT_CURL_OPTIONS,
      user_agent: user_agent
    )

    while stdout.match?(/\AHTTP.*\r$/)
      h, stdout = stdout.split("\r\n\r\n", 2)

      headers << h.split("\r\n").drop(1)
                  .map { |header| header.split(/:\s*/, 2) }
                  .to_h.transform_keys(&:downcase)
    end

    return headers if status.success?
  end

  headers
end