diff --git a/Library/Homebrew/livecheck/strategy.rb b/Library/Homebrew/livecheck/strategy.rb index eb49b0dc04..69a139e161 100644 --- a/Library/Homebrew/livecheck/strategy.rb +++ b/Library/Homebrew/livecheck/strategy.rb @@ -147,6 +147,7 @@ end require_relative "strategy/apache" require_relative "strategy/bitbucket" require_relative "strategy/cpan" +require_relative "strategy/electron_builder" require_relative "strategy/git" require_relative "strategy/github_latest" require_relative "strategy/gnome" diff --git a/Library/Homebrew/livecheck/strategy/electron_builder.rb b/Library/Homebrew/livecheck/strategy/electron_builder.rb new file mode 100644 index 0000000000..551951d841 --- /dev/null +++ b/Library/Homebrew/livecheck/strategy/electron_builder.rb @@ -0,0 +1,75 @@ +# typed: false +# frozen_string_literal: true + +module Homebrew + module Livecheck + module Strategy + # The {ElectronBuilder} strategy fetches content at a URL and parses + # it as an electron-builder appcast in YAML format. + # + # @api private + class ElectronBuilder + extend T::Sig + + NICE_NAME = "electron-builder" + + # A priority of zero causes livecheck to skip the strategy. We do this + # for {ElectronBuilder} so we can selectively apply the strategy using + # `strategy :electron_builder` in a `livecheck` block. + PRIORITY = 0 + + # The `Regexp` used to determine if the strategy applies to the URL. + URL_MATCH_REGEX = %r{^https?://.+?/.+?\.yml}i.freeze + + # Whether the strategy can be applied to the provided URL. + # + # @param url [String] the URL to match against + # @return [Boolean] + sig { params(url: String).returns(T::Boolean) } + def self.match?(url) + URL_MATCH_REGEX.match?(url) + end + + # Extract version information from page content. + # + # @param content [String] the content to check + # @return [String] + sig { params(content: String).returns(T.nilable(String)) } + def self.version_from_content(content) + require "yaml" + + return unless (item = YAML.safe_load(content)) + + item["version"] + end + + # Checks the content at the URL for new versions. + # + # @param url [String] the URL of the content to check + # @param regex [Regexp] a regex used for matching versions in content + # @return [Hash] + sig { params(url: String, regex: T.nilable(Regexp)).returns(T::Hash[Symbol, T.untyped]) } + def self.find_versions(url, regex = nil, &block) + raise ArgumentError, "The #{T.must(name).demodulize} strategy does not support a regex." if regex + + match_data = { matches: {}, regex: regex, url: url } + + match_data.merge!(Strategy.page_content(url)) + content = match_data.delete(:content) + + if (item = version_from_content(content)) + match = if block + block.call(item)&.to_s + else + item + end + + match_data[:matches][match] = Version.new(match) if match + end + + match_data + end + end + end + end +end diff --git a/Library/Homebrew/test/livecheck/strategy/electron_builder_spec.rb b/Library/Homebrew/test/livecheck/strategy/electron_builder_spec.rb new file mode 100644 index 0000000000..fa4a503435 --- /dev/null +++ b/Library/Homebrew/test/livecheck/strategy/electron_builder_spec.rb @@ -0,0 +1,50 @@ +# typed: false +# frozen_string_literal: true + +require "livecheck/strategy/electron_builder" + +describe Homebrew::Livecheck::Strategy::ElectronBuilder do + subject(:electron_builder) { described_class } + + let(:valid_url) { "https://www.example.com/example/latest-mac.yml" } + let(:invalid_url) { "https://www.example.com/example/example" } + + let(:electron_builder_yaml) { + <<~EOS + version: 1.2.3 + files: + - url: Example-1.2.3-mac.zip + sha512: MDXR0pxozBJjxxbtUQJOnhiaiiQkryLAwtcVjlnNiz30asm/PtSxlxWKFYN3kV/kl+jriInJrGypuzajTF6XIA== + size: 92031237 + blockMapSize: 96080 + - url: Example-1.2.3.dmg + sha512: k6WRDlZEfZGZHoOfUShpHxXZb5p44DRp+FAO2FXNx2kStZvyW9VuaoB7phPMfZpcMKrzfRfncpP8VEM8OB2y9g== + size: 94972630 + path: Example-1.2.3-mac.zip + sha512: MDXR0pxozBJjxxbtUQJOnhiaiiQkryLAwtcVjlnNiz30asm/PtSxlxWKFYN3kV/kl+jriInJrGypuzajTF6XIA== + releaseDate: '2000-01-01T00:00:00.000Z' + EOS + } + + describe "::match?" do + it "returns true for any URL pointing to a YAML file" do + expect(electron_builder.match?(valid_url)).to be true + end + + it "returns false for a URL not pointing to a YAML file" do + expect(electron_builder.match?(invalid_url)).to be false + end + end + + describe "::version_from_content" do + let(:version_from_electron_builder_yaml) { electron_builder.version_from_content(electron_builder_yaml) } + + it "returns nil if content is blank" do + expect(electron_builder.version_from_content("")).to be nil + end + + it "returns a version string when given YAML data" do + expect(version_from_electron_builder_yaml).to be_a(String) + end + end +end