 e56735a171
			
		
	
	
		e56735a171
		
			
		
	
	
	
	
		
			
			The `ElectronBuilder` strategy uses `YAML#safe_load` to parse YAML content and this limits deserialization to appropriate classes. We recently encountered a `Tried to load unspecified class: Time` error when using the `ElectronBuilder` strategy on a `latest-mac.yml` file containing `releaseDate: 2022-12-01T02:02:46.419Z`. The electron-builder YAML files we usually encounter use single quotes around the `releaseDate` value to ensure it's treated as a string (e.g., `releaseDate: '2022-10-12T17:55:26.718Z'`) and this is what we do in `electron_builder_spec.rb`. The aforementioned YAML file doesn't use single quotes around the value, so it's treated as a timestamp and apparently this makes Psych use `Time` (which `#safe_load` doesn't allow by default). Seeing as we can't control the YAML content and there's a chance we may encounter other files like this in the future, this commit modifies the related `#safe_load` call to allow `Time` (and `Date` for good measure). This will resolve the aforementioned error and allow the `ElectronBuilder` strategy to work as expected in this scenario.
		
			
				
	
	
		
			94 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
			
		
		
	
	
			94 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
| # typed: true
 | |
| # 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.
 | |
|       #
 | |
|       # This strategy is not applied automatically and it's necessary to use
 | |
|       # `strategy :electron_builder` in a `livecheck` block to apply it.
 | |
|       #
 | |
|       # @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 it when appropriate.
 | |
|         PRIORITY = 0
 | |
| 
 | |
|         # The `Regexp` used to determine if the strategy applies to the URL.
 | |
|         URL_MATCH_REGEX = %r{^https?://.+/[^/]+\.ya?ml(?:\?[^/?]+)?$}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
 | |
| 
 | |
|         # Parses YAML text and identifies versions in it.
 | |
|         #
 | |
|         # @param content [String] the YAML text to parse and check
 | |
|         # @param regex [Regexp, nil] a regex for use in a strategy block
 | |
|         # @return [Array]
 | |
|         sig {
 | |
|           params(
 | |
|             content: String,
 | |
|             regex:   T.nilable(Regexp),
 | |
|             block:   T.untyped,
 | |
|           ).returns(T::Array[String])
 | |
|         }
 | |
|         def self.versions_from_content(content, regex = nil, &block)
 | |
|           require "yaml"
 | |
| 
 | |
|           yaml = YAML.safe_load(content, permitted_classes: [Date, Time])
 | |
|           return [] if yaml.blank?
 | |
| 
 | |
|           if block
 | |
|             block_return_value = regex.present? ? yield(yaml, regex) : yield(yaml)
 | |
|             return Strategy.handle_block_return(block_return_value)
 | |
|           end
 | |
| 
 | |
|           version = yaml["version"]
 | |
|           version.present? ? [version] : []
 | |
|         end
 | |
| 
 | |
|         # Checks the YAML content at the URL for new versions.
 | |
|         #
 | |
|         # @param url [String] the URL of the content to check
 | |
|         # @return [Hash]
 | |
|         sig {
 | |
|           params(
 | |
|             url:     String,
 | |
|             regex:   T.nilable(Regexp),
 | |
|             _unused: T.nilable(T::Hash[Symbol, T.untyped]),
 | |
|             block:   T.untyped,
 | |
|           ).returns(T::Hash[Symbol, T.untyped])
 | |
|         }
 | |
|         def self.find_versions(url:, regex: nil, **_unused, &block)
 | |
|           if regex.present? && block.blank?
 | |
|             raise ArgumentError, "#{T.must(name).demodulize} only supports a regex when using a `strategy` block"
 | |
|           end
 | |
| 
 | |
|           match_data = { matches: {}, regex: regex, url: url }
 | |
| 
 | |
|           match_data.merge!(Strategy.page_content(url))
 | |
|           content = match_data.delete(:content)
 | |
| 
 | |
|           versions_from_content(content, regex, &block).each do |version_text|
 | |
|             match_data[:matches][version_text] = Version.new(version_text)
 | |
|           end
 | |
| 
 | |
|           match_data
 | |
|         end
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| end
 |