81 Commits

Author SHA1 Message Date
Douglas Eichelberger
8301d39b99
fix: require strategic interface in each livecheck strategy 2025-03-04 11:48:54 -08:00
Douglas Eichelberger
9515714b44
Add interface for livecheck strategies 2025-02-26 16:45:39 -08:00
Sam Ford
b6eb945320
livecheck: Add Options class
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`.
2025-02-25 10:56:31 -05:00
Douglas Eichelberger
936b9b5369
Move nil check inside demodulize 2025-02-24 11:57:20 -08:00
Sam Ford
a16f5666a8
Sparkle: Add homebrew_curl support
`Sparkle` is the only strategy with a `find_versions` method that
calls `Strategy::page_content` (or `::page_headers`) and doesn't have
a `homebrew_curl` parameter. This adds the missing parameter and
passes the value to `page_content`, which brings it in line with the
other strategies.
2025-02-07 08:57:32 -05:00
Sam Ford
b4757af656
livecheck: Add support for POST requests
livecheck currently doesn't support `POST` requests but it wasn't
entirely clear how best to handle that. I initially approached it as
a `Post` strategy but unfortunately that would have required us to
handle response body parsing (e.g., JSON, XML, etc.) in some fashion.
We could borrow some of the logic from related strategies but we would
still be stuck having to update `Post` whenever we add a strategy for
a new format.

Instead, this implements `POST` support by borrowing ideas from the
`using: :post` and `data` `url` options found in formulae. This uses
a `post_form` option to handle form data and `post_json` to handle
JSON data, encoding the hash argument for each into the appropriate
format. The presence of either option means that curl will use a
`POST` request.

With this approach, we can make a `POST` request using any strategy
that calls `Strategy::page_headers` or `::page_content` (directly or
indirectly) and everything else works the same as usual. The only
change needed in related strategies was to pass the options through
to the `Strategy` methods.

For example, if we need to parse a JSON response from a `POST`
request, we add a `post_data` or `post_json` hash to the `livecheck`
block `url` and use `strategy :json` with a `strategy` block. This
leans on existing patterns that we're already familiar with and
shouldn't require any notable maintenance burden when adding new
strategies, so it seems like a better approach than a `Post` strategy.
2025-02-07 08:53:47 -05:00
Sam Ford
7ba189a018
livecheck: expand typed: strict usage
This updates livecheck files to use `typed: script` where feasible.

The remaining exception is `livecheck/strategy.rb`, as I wasn't
able to figure out how to resolve the typing issues around the
`@strategies` variable (I tried a couple of approaches but couldn't
find a working solution). This includes changes to resolve the other
type errors in `strategy.rb` but leaves the file as `typed: true`
for now.
2024-07-04 20:22:14 -04:00
Markus Reiter
caf87c0336
Warn about undocumented non-private APIs. 2024-05-01 11:35:20 +02:00
Markus Reiter
0f0055ede4
Make documentation @api private by default. 2024-04-26 19:04:20 +02:00
Mike McQuaid
ea2892f8ee
brew.rb: handle missing args. 2024-03-07 16:20:20 +00:00
Michael Cho
89b6d471e5
livecheck/strategy: fix **unused type 2024-02-28 12:32:21 -05:00
Issy Long
f4218a6316
Fix RuboCop Performance/MapCompact offenses
- Rename an iterator variable since it would make the line too long.
2024-02-25 22:59:59 +00:00
Issy Long
f682147598
Fix RuboCop Style/RedundantFreeze offenses 2024-01-18 22:20:01 +00:00
Sam Ford
9bfe423a5a
Xml: Add #element_text method
This refactors verbose code in the `Sparkle` strategy where we access
element text into a reusable `Xml#element_text` method, replacing
chained calls like `item.elements["title"]&.text&.strip&.presence`
with `Xml.element_text(item, "title")`.

`#element_text` is only used to retrieve the text of a child element
in the `Sparkle` strategy but it can also retrieve the text from the
provided element if the `child_path` argument is omitted (i.e.,
`Xml.element_text(item)`). This will allow us to also avoid similar
calls like `item.text.strip.presence` in the future.
2023-11-17 22:34:12 -05:00
Sam Ford
75ce7240fc
Sparkle: Refactor macOS os strings into constant 2023-11-16 12:05:25 -05:00
Sam Ford
bc2ce97e5d
Sparkle: Move sorting/filtering into methods
We need to be able to replicate the `Sparkle` strategy's sorting
and filtering behavior in a related cask audit, so this extracts
the logic into reusable methods.

This also stores `item.minimum_system_version` as a `MacOSVersion`
object (instead of a string), so we can do proper version comparison
(instead of naive string comparison) wherever needed.
2023-11-16 12:05:24 -05:00
Sam Ford
4f469234d0
Sparkle: Ensure empty strings become nil
Sometimes appcasts contain empty elements/attributes and the `Item`
values end up as an empty string because of how they're handled in
`#items_from_content`. It's reasonable to expect that empty values
would be `nil` instead, so this adds `#presence` calls to ensure this
is the case.
2023-11-16 12:05:24 -05:00
Sam Ford
86c702abcd
Sparkle: Surface more Item values
Historically, the `Sparkle` strategy's `Item` struct has only
included basic values from the appcast that are commonly useful.
Over time we've selectively added/surfaced more values as we've
encountered outliers that require use of different values in a
`strategy` block.

We now need to use `minimumSystemValue`, so this expands the `Item`
struct to include any appcast value that we could conceivably want
to use in the future. This will hopefully save us from having to make
more modifications to the struct (and related tests) before we can
use a previously-unused value in a `strategy` block.
2023-10-28 14:55:47 -04:00
Bo Anderson
26ea6b7f9e
Pull in REXML gem as it doesn't ship with Ruby 3 2023-10-27 20:24:10 +01:00
Patrick Linnane
c56669e9cd
various: fix miscellaneous typos 2023-07-18 08:52:49 -07:00
Markus Reiter
8274920217
Rename OS::Mac::Version to MacOSVersion. 2023-05-09 05:08:38 +02:00
Sam Ford
6597ee6fd3
Sparkle: Account for nil content value
`content` can be `nil` when a request doesn't succeed but
`#versions_from_content` expects a `String` value, so we need to
guard against a `nil` value like we do in other strategies.
2023-04-29 10:16:47 -04:00
Douglas Eichelberger
24cf6076e8 brew style --fix 2023-04-24 20:42:39 -07:00
Douglas Eichelberger
47ffcd5cb7 Add types to block params 2023-04-04 22:42:27 -07:00
Sam Ford
7d9e1a50e8
livecheck: Add Xml strategy
This adds a generic `Xml` strategy to livecheck that requires a
`strategy` block to operate. The XML-parsing code is taken from the
existing approach in the `Sparkle` strategy. As such, `Sparkle` has
been updated to use the `Xml#parse_xml` method instead.

Unlike the `Json` strategy, we don't currently have any `strategy`
blocks in first-party taps that manually parse XML. However, we had a
user request support for something like this and I was already working
on an `Xml` strategy (as a way of extracting the XML-parsing code
from `Sparkle` into something general-purpose), so here we are.

Future strategies that parse simple XML data can potentially use the
`Xml#find_versions` method (similar to how we have strategies that
leverage `PageMatch#find_versions`) instead of having to implement
something bespoke like `Sparkle`.
2023-02-28 14:58:11 -05:00
Douglas Eichelberger
0eccc0e987 git grep -l Utils::Inflection | xargs gsed -i 's|Utils::Inflection|Utils|g' 2023-02-27 20:18:27 -08:00
Douglas Eichelberger
2400c6daed brew style --fix 2023-02-27 20:17:33 -08:00
Douglas Eichelberger
d62211d3af Replace demodulize inflection with util 2023-02-27 20:17:33 -08:00
Tim Visher
fc31d5560c livecheck/strategy/sparkle.rb: Add macos to the candidate os values list
As can be seen by TextExpander's Sparkle Feed, `macos` is a possible
value.

```
$ date -u '+%FT%T%z' && xmlstarlet sel -t -v '//rss//channel//item//enclosure/@*[name()="url" or name()="sparkle:version" or name()="sparkle:shortVersionString" or name()="sparkle:os"]' <(curl --location --silent https://textexpander.com/appcast/TextExpander-macOS.xml)
2022-10-22T17:07:06+0000
https://cdn.textexpander.com/mac/731.2/TextExpander_7.3.1.dmg
731.2
7.3.1
macos
https://cdn.textexpander.com/mac/720.16/TextExpander_7.2.dmg
720.16
7.2
macos
https://cdn.textexpander.com/mac/710.6/TextExpander_7.1.dmg
710.6
7.1
macos
https://cdn.textexpander.com/mac/702.2/TextExpander_7.0.2.dmg
702.2
7.0.2
macos
https://cdn.textexpander.com/mac/701.2/TextExpander_7.0.1.dmg
701.2
7.0.1
macos
https://cdn.textexpander.com/mac/700.33/TextExpander_7.0.dmg
700.33
7.0
macos
https://cdn.textexpander.com/mac/685.6/TextExpander_6.8.5.zip
685.6
6.8.5
https://cdn.textexpander.com/mac/684.8/TextExpander_6.8.4.zip
684.8
6.8.4
https://cdn.textexpander.com/mac/683.2/TextExpander_6.8.3.zip
683.2
6.8.3
https://cdn.textexpander.com/mac/682.10/TextExpander_6.8.2.zip
682.10
6.8.2
https://cdn.textexpander.com/mac/681.3/TextExpander_6.8.1.zip
681.3
6.8.1
https://cdn.textexpander.com/mac/680.30/TextExpander_6.8.zip
680.30
6.8
https://cdn.textexpander.com/mac/TextExpander_6.5.6.zip
656.3
6.5.6
https://cdn.textexpander.com/mac/TextExpander_6.5.5.zip
655.0
6.5.5
https://cdn.textexpander.com/mac/TextExpander_6.5.4.zip
654.3
6.5.4
https://cdn.textexpander.com/mac/TextExpander_6.5.3.zip
653.3
6.5.3
https://cdn.textexpander.com/mac/TextExpander_6.5.2.zip
652.0
6.5.2
https://smilesoftware.com/downloads/test/TextExpander_6.5.1.zip
651.5
6.5.1
```

Co-authored-by: Sam Ford <1584702+samford@users.noreply.github.com>
2022-12-04 14:21:27 -05:00
Bo Anderson
eac32f0b20
Additional fixes for Ruby 3 compatibility 2022-10-18 01:54:59 +01:00
Bo Anderson
f5dc7a571f
Fix Style/ObjectThen offences 2022-06-17 19:47:55 +01:00
Sam Ford
d600b662e2
Expand documentation comments 2022-06-10 18:25:25 -04:00
Sam Ford
590aabe9d7
livecheck: update default match_data
This updates these strategies to better align with the default
`match_data` value in other strategies.
2022-06-10 18:25:25 -04:00
Sam Ford
c6907f911f
Sparkle: Provide channel information in Item
Making `channel` information available in the `Item` is necessary
to be able to filter out unstable items using a `strategy` block. If
an item doesn't specify a channel, then it uses the default channel
(this is what Sparkle itself uses for updates). Channels like `beta`
are something we want to avoid for stable casks and this allows for
that type of [cask-specific] filtering.

It's technically possible to automatically filter out items that
aren't using the default channel (i.e., `channel != nil`) in
`#items_from_content` but some casks use an unstable version, so we
can't do this internally. That is to say, we wouldn't be able to
override internal filtering in a `strategy` block, as any omitted
items wouldn't be provided to the block. Conversely, if we pass all
items to a `strategy` block, we can easily filter by channel there.
We haven't been filtering by channel internally and we've only found
one cask where this has been a problem, so it seems fine for now.
2022-06-02 10:35:00 -04:00
Sam Ford
b4cb47815f
Sparkle: Pass all items into strategy block
It's sometimes necessary to work with all the items in a Sparkle feed
to be able to correctly identify the newest version but livecheck's
`Sparkle` strategy only passes the `item` it views as newest into a
`strategy` block.

This updates the `Sparkle` strategy to optionally pass all items into
a `strategy` block, so we can manipulate them (e.g., filtering,
sorting). This is enabled by naming the first argument of the
strategy block `items` instead of `item`. `Sparkle` `strategy` blocks
where the first argument is `item` will continue to work as expected.

This necessarily updates `#item_from_content` (now
`items_from_content`) to return all items. I've decided to move the
sorting out of `#items_from_content`, so it simply returns the items
in the order they appear. If there is ever an exceptional situation
where we need the original order, this will technically allow for it.

The sorting has instead been moved into the `#versions_from_content`
method, to maintain the existing behavior. I thought about passing
the items into the `strategy` block in their original order but it
feels like sorting by default is the better approach for now (partly
from the perspective of maintaining existing behavior) and we can
always revisit this in the future if a cask ever requires the
original order.

Lastly, this expands the `Sparkle` tests to increase coverage. The
only untested parts are `#find_versions` (which currently
requires a network request) and a couple safeguard `raise` calls
when there's a `REXML::UndefinedNamespaceException` (which shouldn't
be encountered unless something is broken).
2022-06-01 18:29:37 -04:00
Sam Ford
085827f605
Handle variable strategy block arguments
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.
2021-11-19 23:58:55 -05:00
Sam Ford
9e8900fb6e
Allow regex to be passed into all strategy blocks
This modifies cask-related livecheck strategies to allow passing a
regex into a `strategy` block, when appropriate. These strategies
were outliers that explicitly rejected a regex even if a `strategy`
block was used, forcing any regex to be inlined in the `strategy`
block (instead of being defined using `#regex`).

With these changes, all `strategy` blocks will be able to accept a
regex, further simplifying the mental model. This also helps to
better align the various `find_versions` and `versions_from_*`
methods across strategies.
2021-11-19 23:58:54 -05:00
Mike McQuaid
d43555dd75
brew style --fix 2021-11-02 11:16:10 +00:00
Sam Ford
13b349b4ae
Refactor #find_versions parameters in strategies 2021-08-16 12:01:32 -04:00
Sam Ford
c936a9420e
Improve documentation comments 2021-08-11 19:05:59 -04:00
Sam Ford
56dd89114d
Standardize valid strategy block return types
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.
2021-08-11 19:05:57 -04:00
Sam Ford
a970780851
livecheck: allow nil return from strategy blocks 2021-08-02 18:33:34 -04:00
Sam Ford
87564b6ccb
Sparkle: Use OS::Mac::Version#prerelease? 2021-06-28 16:54:53 -04:00
Sam Ford
4433f4a985
Sparkle: Update minimumSystemVersion condition 2021-06-28 15:48:35 -04:00
Michael Cho
823dc28c21
livecheck/strategy/sparkle: check minimumSystemVersion for compatibility 2021-06-28 15:39:19 -04:00
Sam Ford
2c4a7ae2cb
Sparkle: Replace Nokogiri with REXML 2021-06-25 17:45:25 -04:00
Sam Ford
23f8cb9f4a
Sparkle: Handle ArgumentError from Time#parse 2021-06-22 10:02:57 -04:00
Sam Ford
a5768de0c3
Sparkle: Account for empty pubDate 2021-06-22 09:23:56 -04:00
Sam Ford
e7d3b2cb31
Sparkle: Move default pub_date value 2021-06-22 09:14:56 -04:00
hyuraku
6720f8bd1e
repair pub-date in livecheck 2021-06-22 09:14:55 -04:00