| 
									
										
										
										
											2024-06-02 15:14:25 +01:00
										 |  |  | # typed: strict | 
					
						
							| 
									
										
										
										
											2023-03-02 15:19:00 -05:00
										 |  |  | # frozen_string_literal: true | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module Homebrew | 
					
						
							|  |  |  |   module Livecheck | 
					
						
							|  |  |  |     module Strategy | 
					
						
							| 
									
										
										
										
											2024-04-30 11:10:23 +02:00
										 |  |  |       # The {Yaml} strategy fetches content at a URL, parses it as YAML and | 
					
						
							| 
									
										
										
										
											2023-03-02 15:19:00 -05:00
										 |  |  |       # provides the parsed data to a `strategy` block. If a regex is present | 
					
						
							|  |  |  |       # in the `livecheck` block, it should be passed as the second argument to | 
					
						
							|  |  |  |       # the `strategy` block. | 
					
						
							|  |  |  |       # | 
					
						
							|  |  |  |       # This is a generic strategy that doesn't contain any logic for finding | 
					
						
							|  |  |  |       # versions, as the structure of YAML data varies. Instead, a `strategy` | 
					
						
							|  |  |  |       # block must be used to extract version information from the YAML data. | 
					
						
							|  |  |  |       # | 
					
						
							|  |  |  |       # This strategy is not applied automatically and it is necessary to use | 
					
						
							|  |  |  |       # `strategy :yaml` in a `livecheck` block (in conjunction with a | 
					
						
							|  |  |  |       # `strategy` block) to use it. | 
					
						
							|  |  |  |       # | 
					
						
							|  |  |  |       # This strategy's {find_versions} method can be used in other strategies | 
					
						
							|  |  |  |       # that work with YAML content, so it should only be necessary to write | 
					
						
							|  |  |  |       # the version-finding logic that works with the parsed YAML data. | 
					
						
							|  |  |  |       # | 
					
						
							|  |  |  |       # @api public | 
					
						
							|  |  |  |       class Yaml | 
					
						
							|  |  |  |         NICE_NAME = "YAML" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # A priority of zero causes livecheck to skip the strategy. We do this | 
					
						
							|  |  |  |         # for {Yaml} so we can selectively apply it only when a strategy block | 
					
						
							|  |  |  |         # is provided in a `livecheck` block. | 
					
						
							|  |  |  |         PRIORITY = 0
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # The `Regexp` used to determine if the strategy applies to the URL. | 
					
						
							| 
									
										
										
										
											2024-01-18 22:18:42 +00:00
										 |  |  |         URL_MATCH_REGEX = %r{^https?://}i | 
					
						
							| 
									
										
										
										
											2023-03-02 15:19:00 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Whether the strategy can be applied to the provided URL. | 
					
						
							|  |  |  |         # {Yaml} will technically match any HTTP URL but is only usable with | 
					
						
							|  |  |  |         # a `livecheck` block containing a `strategy` block. | 
					
						
							|  |  |  |         # | 
					
						
							|  |  |  |         # @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 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Parses YAML text and returns the parsed data. | 
					
						
							|  |  |  |         # @param content [String] the YAML text to parse | 
					
						
							|  |  |  |         sig { params(content: String).returns(T.untyped) } | 
					
						
							|  |  |  |         def self.parse_yaml(content) | 
					
						
							|  |  |  |           require "yaml" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           begin | 
					
						
							|  |  |  |             YAML.safe_load(content, permitted_classes: [Date, Time]) | 
					
						
							|  |  |  |           rescue Psych::SyntaxError | 
					
						
							|  |  |  |             raise "Content could not be parsed as YAML." | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Parses YAML text and identifies versions using a `strategy` block. | 
					
						
							|  |  |  |         # If a regex is provided, it will be passed as the second argument to | 
					
						
							|  |  |  |         # the  `strategy` block (after the parsed YAML data). | 
					
						
							|  |  |  |         # @param content [String] the YAML text to parse and check | 
					
						
							|  |  |  |         # @param regex [Regexp, nil] a regex used for matching versions in the | 
					
						
							|  |  |  |         #   content | 
					
						
							|  |  |  |         # @return [Array] | 
					
						
							|  |  |  |         sig { | 
					
						
							|  |  |  |           params( | 
					
						
							|  |  |  |             content: String, | 
					
						
							|  |  |  |             regex:   T.nilable(Regexp), | 
					
						
							| 
									
										
										
										
											2023-04-04 22:40:31 -07:00
										 |  |  |             block:   T.nilable(Proc), | 
					
						
							| 
									
										
										
										
											2023-03-02 15:19:00 -05:00
										 |  |  |           ).returns(T::Array[String]) | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         def self.versions_from_content(content, regex = nil, &block) | 
					
						
							|  |  |  |           return [] if content.blank? || block.blank? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           yaml = parse_yaml(content) | 
					
						
							|  |  |  |           return [] if yaml.blank? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           block_return_value = if regex.present? | 
					
						
							|  |  |  |             yield(yaml, regex) | 
					
						
							|  |  |  |           elsif block.arity == 2
 | 
					
						
							|  |  |  |             raise "Two arguments found in `strategy` block but no regex provided." | 
					
						
							|  |  |  |           else | 
					
						
							|  |  |  |             yield(yaml) | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |           Strategy.handle_block_return(block_return_value) | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Checks the YAML content at the URL for versions, using the provided | 
					
						
							|  |  |  |         # `strategy` block to extract version information. | 
					
						
							|  |  |  |         # | 
					
						
							|  |  |  |         # @param url [String] the URL of the content to check | 
					
						
							|  |  |  |         # @param regex [Regexp, nil] a regex used for matching versions | 
					
						
							|  |  |  |         # @param provided_content [String, nil] page content to use in place of | 
					
						
							|  |  |  |         #   fetching via `Strategy#page_content` | 
					
						
							|  |  |  |         # @param homebrew_curl [Boolean] whether to use brewed curl with the URL | 
					
						
							|  |  |  |         # @return [Hash] | 
					
						
							|  |  |  |         sig { | 
					
						
							|  |  |  |           params( | 
					
						
							|  |  |  |             url:              String, | 
					
						
							|  |  |  |             regex:            T.nilable(Regexp), | 
					
						
							|  |  |  |             provided_content: T.nilable(String), | 
					
						
							|  |  |  |             homebrew_curl:    T::Boolean, | 
					
						
							| 
									
										
										
										
											2024-02-28 12:32:21 -05:00
										 |  |  |             _unused:          T.untyped, | 
					
						
							| 
									
										
										
										
											2023-04-04 22:40:31 -07:00
										 |  |  |             block:            T.nilable(Proc), | 
					
						
							| 
									
										
										
										
											2023-03-02 15:19:00 -05:00
										 |  |  |           ).returns(T::Hash[Symbol, T.untyped]) | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         def self.find_versions(url:, regex: nil, provided_content: nil, homebrew_curl: false, **_unused, &block) | 
					
						
							|  |  |  |           raise ArgumentError, "#{Utils.demodulize(T.must(name))} requires a `strategy` block" if block.blank? | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-07 16:20:20 +00:00
										 |  |  |           match_data = { matches: {}, regex:, url: } | 
					
						
							| 
									
										
										
										
											2023-03-02 15:19:00 -05:00
										 |  |  |           return match_data if url.blank? || block.blank? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           content = if provided_content.is_a?(String) | 
					
						
							|  |  |  |             match_data[:cached] = true | 
					
						
							|  |  |  |             provided_content | 
					
						
							|  |  |  |           else | 
					
						
							| 
									
										
										
										
											2024-03-07 16:20:20 +00:00
										 |  |  |             match_data.merge!(Strategy.page_content(url, homebrew_curl:)) | 
					
						
							| 
									
										
										
										
											2023-03-02 15:19:00 -05:00
										 |  |  |             match_data[:content] | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |           return match_data if content.blank? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           versions_from_content(content, regex, &block).each do |match_text| | 
					
						
							|  |  |  |             match_data[:matches][match_text] = Version.new(match_text) | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           match_data | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | end |