 50384591c7
			
		
	
	
		50384591c7
		
			
		
	
	
	
	
		
			
			Add these and correctly pass through a string to `GitHub::Actions.puts_annotation_if_env_set`. Also, fix some call sites to not rely on the `void` return.
		
			
				
	
	
		
			182 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
			
		
		
	
	
			182 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
| # typed: strict
 | |
| # frozen_string_literal: true
 | |
| 
 | |
| require "timeout"
 | |
| require "cask/download"
 | |
| require "cask/installer"
 | |
| require "cask/cask_loader"
 | |
| require "system_command"
 | |
| require "tap"
 | |
| require "unversioned_cask_checker"
 | |
| 
 | |
| module Homebrew
 | |
|   module DevCmd
 | |
|     class BumpUnversionedCasks < AbstractCommand
 | |
|       include SystemCommand::Mixin
 | |
| 
 | |
|       cmd_args do
 | |
|         description <<~EOS
 | |
|           Check all casks with unversioned URLs in a given <tap> for updates.
 | |
|         EOS
 | |
|         switch "-n", "--dry-run",
 | |
|                description: "Do everything except caching state and opening pull requests."
 | |
|         flag   "--limit=",
 | |
|                description: "Maximum runtime in minutes."
 | |
|         flag   "--state-file=",
 | |
|                description: "File for caching state."
 | |
| 
 | |
|         named_args [:cask, :tap], min: 1, without_api: true
 | |
|       end
 | |
| 
 | |
|       sig { override.void }
 | |
|       def run
 | |
|         Homebrew.install_bundler_gems!(groups: ["bump_unversioned_casks"])
 | |
| 
 | |
|         state_file = if args.state_file.present?
 | |
|           Pathname(T.must(args.state_file)).expand_path
 | |
|         else
 | |
|           HOMEBREW_CACHE/"bump_unversioned_casks.json"
 | |
|         end
 | |
|         state_file.dirname.mkpath
 | |
| 
 | |
|         state = state_file.exist? ? JSON.parse(state_file.read) : {}
 | |
| 
 | |
|         casks = args.named.to_paths(only: :cask, recurse_tap: true).map { |path| Cask::CaskLoader.load(path) }
 | |
| 
 | |
|         unversioned_casks = casks.select do |cask|
 | |
|           cask.url&.unversioned? && !cask.livecheckable?
 | |
|         end
 | |
| 
 | |
|         ohai "Unversioned Casks: #{unversioned_casks.count} (#{state.size} cached)"
 | |
| 
 | |
|         checked, unchecked = unversioned_casks.partition { |c| state.key?(c.full_name) }
 | |
| 
 | |
|         queue = Queue.new
 | |
| 
 | |
|         # Start with random casks which have not been checked.
 | |
|         unchecked.shuffle.each do |c|
 | |
|           queue.enq c
 | |
|         end
 | |
| 
 | |
|         # Continue with previously checked casks, ordered by when they were last checked.
 | |
|         checked.sort_by { |c| state.dig(c.full_name, "check_time") }.each do |c|
 | |
|           queue.enq c
 | |
|         end
 | |
| 
 | |
|         limit = args.limit.presence&.to_i
 | |
|         end_time = Time.now + (limit * 60) if limit
 | |
| 
 | |
|         until queue.empty? || (end_time && end_time < Time.now)
 | |
|           cask = queue.deq
 | |
| 
 | |
|           key = cask.full_name
 | |
| 
 | |
|           new_state = bump_unversioned_cask(cask, state: state.fetch(key, {}))
 | |
| 
 | |
|           next unless new_state
 | |
| 
 | |
|           state[key] = new_state
 | |
| 
 | |
|           state_file.atomic_write JSON.pretty_generate(state) unless args.dry_run?
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       private
 | |
| 
 | |
|       sig {
 | |
|         params(cask: Cask::Cask, state: T::Hash[String, T.untyped])
 | |
|           .returns(T.nilable(T::Hash[String, T.untyped]))
 | |
|       }
 | |
|       def bump_unversioned_cask(cask, state:)
 | |
|         ohai "Checking #{cask.full_name}"
 | |
| 
 | |
|         unversioned_cask_checker = UnversionedCaskChecker.new(cask)
 | |
| 
 | |
|         if !unversioned_cask_checker.single_app_cask? &&
 | |
|            !unversioned_cask_checker.single_pkg_cask? &&
 | |
|            !unversioned_cask_checker.single_qlplugin_cask?
 | |
|           opoo "Skipping, not a single-app or PKG cask."
 | |
|           return
 | |
|         end
 | |
| 
 | |
|         last_check_time = state["check_time"]&.then { |t| Time.parse(t) }
 | |
| 
 | |
|         check_time = Time.now
 | |
|         if last_check_time && (check_time - last_check_time) / 3600 < 24
 | |
|           opoo "Skipping, already checked within the last 24 hours."
 | |
|           return
 | |
|         end
 | |
| 
 | |
|         last_sha256 = state["sha256"]
 | |
|         last_time = state["time"]&.then { |t| Time.parse(t) }
 | |
|         last_file_size = state["file_size"]
 | |
| 
 | |
|         download = Cask::Download.new(cask)
 | |
|         time, file_size = begin
 | |
|           download.time_file_size
 | |
|         rescue
 | |
|           [nil, nil]
 | |
|         end
 | |
| 
 | |
|         if last_time != time || last_file_size != file_size
 | |
|           sha256 = begin
 | |
|             Timeout.timeout(5 * 60) do
 | |
|               unversioned_cask_checker.installer.download.sha256
 | |
|             end
 | |
|           rescue => e
 | |
|             onoe e
 | |
| 
 | |
|             nil
 | |
|           end
 | |
| 
 | |
|           if sha256.present? && last_sha256 != sha256
 | |
|             version = begin
 | |
|               Timeout.timeout(60) do
 | |
|                 unversioned_cask_checker.guess_cask_version
 | |
|               end
 | |
|             rescue Timeout::Error
 | |
|               onoe "Timed out guessing version for cask '#{cask}'."
 | |
| 
 | |
|               nil
 | |
|             end
 | |
| 
 | |
|             if version
 | |
|               if cask.version == version
 | |
|                 oh1 "Cask #{cask} is up-to-date at #{version}"
 | |
|               else
 | |
|                 bump_cask_pr_args = [
 | |
|                   "bump-cask-pr",
 | |
|                   "--version", version.to_s,
 | |
|                   "--sha256", ":no_check",
 | |
|                   "--message", "Automatic update via `brew bump-unversioned-casks`.",
 | |
|                   cask.sourcefile_path
 | |
|                 ]
 | |
| 
 | |
|                 if args.dry_run?
 | |
|                   bump_cask_pr_args << "--dry-run"
 | |
|                   oh1 "Would bump #{cask} from #{cask.version} to #{version}"
 | |
|                 else
 | |
|                   oh1 "Bumping #{cask} from #{cask.version} to #{version}"
 | |
|                 end
 | |
| 
 | |
|                 begin
 | |
|                   system_command! HOMEBREW_BREW_FILE, args: bump_cask_pr_args
 | |
|                 rescue ErrorDuringExecution => e
 | |
|                   onoe e
 | |
|                 end
 | |
|               end
 | |
|             end
 | |
|           end
 | |
|         end
 | |
| 
 | |
|         {
 | |
|           "sha256"     => sha256,
 | |
|           "check_time" => check_time.iso8601,
 | |
|           "time"       => time&.iso8601,
 | |
|           "file_size"  => file_size,
 | |
|         }
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| end
 |