This adds a `Livecheck::Options` class, which is intended to house
various configuration options that are set in `livecheck` blocks,
conditionally set by livecheck at runtime, etc. The general idea is
that when we add features involving configurations options (e.g., for
livecheck, strategies, curl, etc.), we can make changes to `Options`
without needing to modify parameters for strategy `find_versions`
methods, `Strategy` methods like `page_headers` and `page_content`,
etc. This is something that I've been trying to improve over the years
and `Options` should help to reduce maintenance overhead in this area
while also strengthening type signatures.
`Options` replaces the existing `homebrew_curl` option (which related
strategies pass to `Strategy` methods and on to `curl_args`) and the
new `url_options` (which contains `post_form` or `post_json` values
that are used to make `POST` requests). I recently added `url_options`
as a temporary way of enabling `POST` support without `Options` but
this restores the original `Options`-based implementation.
Along the way, I added a `homebrew_curl` parameter to the `url` DSL
method, allowing us to set an explicit value in `livecheck` blocks.
This is something that we've needed in some cases but I also intend
to replace implicit/inferred `homebrew_curl` usage with explicit
values in `livecheck` blocks once this is available for use. My
intention is to eventually remove the implicit behavior and only rely
on explicit values. That will align with how `homebrew_curl` options
work for other URLs and makes the behavior clear just from looking at
the `livecheck` block.
Lastly, this removes the `unused` rest parameter from `find_versions`
methods. I originally added `unused` as a way of handling parameters
that some `find_versions` methods have but others don't (e.g., `cask`
in `ExtractPlist`), as this allowed us to pass various arguments to
`find_versions` methods without worrying about whether a particular
parameter is available. This isn't an ideal solution and I originally
wanted to handle this situation by only passing expected arguments to
`find_versions` methods but there was a technical issue standing in
the way. I recently found an answer to the issue, so this also
replaces the existing `ExtractPlist` special case with generic logic
that checks the parameters for a strategy's `find_versions` method
and only passes expected arguments.
Replacing the aforementioned `find_versions` parameters with `Options`
ensures that the remaining parameters are fairly consistent across
strategies and any differences are handled by the aforementioned
logic. Outside of `ExtractPlist`, the only other difference is that
some `find_versions` methods have a `provided_content` parameter but
that's currently only used by tests (though it's intended for caching
support in the future). I will be renaming that parameter to `content`
in an upcoming PR and expanding it to the other strategies, which
should make them all consistent outside of `ExtractPlist`.
`Livecheck#preprocess_url` only contains logic for rewriting Git URLs,
so it makes more sense for this code to be part of the `Git` strategy
instead. Outside of better code organization, this saves us from
having to maintain the list of strategies to skip processing (which
is sometimes forgotten when a new strategy is added) and makes it
easier to do something similar in other strategies as needed.
One thing to note is that `Livecheck#preprocess_url` was previously
called on the URL before each strategy's `#match?` method was called.
To maintain the existing behavior, this calls `Git#preprocess_url` in
`Git#match?`. However, we need the processed URL when we use the `Git`
strategy, so we have to call `Git#preprocess_url` again. To avoid
duplicating effort, I've added a `@processed_urls` hash to the `Git`
strategy and have set up `Git#preprocess_url` to cache processed
URLs, so we only do the work once. There may be a better way of
handling it but this seems to work as expected.
I refactored the `Git` strategy to use `SystemCommand` instead of
`Open3#capture3` in #13387 but I forgot to remove `require "open3"`
at the time. `Git` doesn't use `open3` now, so this removes the
unused `require`.
- This was done with `brew typecheck --update --suggest-typed` which
(as of the previous commit) uses Spoom, yet another gem. I thought I'd
see how well it works. There are no Sorbet errors after these changes!
The `tags_only_debian` code in livecheck's `Git` strategy was
originally introduced in Homebrew/homebrew-livecheck#131 when
livecheck was in a less mature state and relied more on internal
special-casing like this (i.e., while we worked to add appropriate
`livecheck` blocks). This logic only has the potential to be
beneficial when a formula/cask doesn't contain a `livecheck` block
but I would argue that we shouldn't be making assumptions in the
strategy around whether tags with a `debian/` prefix should be
matched or omitted. The answer depends upon the context of a given
formula/cask and should be handled with a `livecheck` block, as we
do with other situations like this.
In a past discussion, it was preferred that we use `system_command`
in livecheck's `Git` strategy instead of `Open3.capture3` but it
wasn't feasible at the time because we couldn't prevent
`#system_command` from printing certain output. I resolved the
`SystemCommand` shortcoming long ago in Homebrew/brew#10066, so this
finally migrates the `Git` strategy to `system_command`.
There are times where a regex isn't needed in a `strategy` block and
these changes explicitly handle that situation.
This allows the Symbol Proc format used in some `Sparkle` `livecheck`
blocks (e.g., `strategy :sparkle, &:version`) to continue working
instead of failing with a "wrong number of arguments (given 1,
expected 0)" error. This error would occur because a Symbol Proc only
only expects one argument (e.g., an `Item`, not an `Item` and a
regex/nil).
There's an argument to be made for avoiding the Symbol Proc format
for `strategy` blocks but I haven't found a way of selectively
disabling the Style/SymbolProc cop only for a `strategy` DSL method
call. That is to say, if we don't use the Symbol Proc format, `brew
style` will give a "Pass &:version as an argument to strategy instead
of a block." offense.
Besides that, this also replaces the `block` type signatures in
livecheck strategies with `T.untyped`. Sorbet doesn't know how to
handle a `Proc` with a variable number of arguments and can't be
taught how (i.e., `T.any` with a `Proc` signature for each variation
doesn't work). The aforementioned changes cause Sorbet to complain
about there being both too many and too few arguments, so the only
way to win is not to play the game. Hopefully we can restore the
`block` type signatures in the future (if upstream resolves this
years-old issue) but `T.untyped` seems to be our only option for now.
Valid `strategy` block return types currently vary between
strategies. Some only accept a string whereas others accept a string
or array of strings. [`strategy` blocks also accept a `nil` return
(to simplify early returns) but this was already standardized across
strategies.]
While some strategies only identify one version by default (where a
string is an appropriate return type), it could be that a strategy
block identifies more than one version. In this situation, the
strategy would need to be modified to accept (and work with) an
array from a `strategy` block.
Rather than waiting for this to become a problem, this modifies all
strategies to standardize on allowing `strategy` blocks to return a
string or array of strings (even if only one of these is currently
used in practice). Standardizing valid return types helps to further
simplify the mental model for `strategy` blocks and reduce cognitive
load.
This commit extracts related logic from `#find_versions` into
methods like `#versions_from_content`, which is conceptually similar
to `PageMatch#page_matches` (renamed to `#versions_from_content`
for consistency). This allows us to write tests for the related code
without having to make network requests (or stub them) at this point.
In general, this also helps to better align the structure of
strategies and how the various `#find_versions` methods work with
versions.
There's still more planned work to be done here but this is a step
in the right direction.