| 
									
										
										
										
											2023-03-18 16:03:10 -07:00
										 |  |  | # typed: true | 
					
						
							| 
									
										
										
										
											2023-03-07 22:17:02 +09:00
										 |  |  | # frozen_string_literal: true | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | require "env_config" | 
					
						
							|  |  |  | require "cask/config" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module Cask | 
					
						
							|  |  |  |   # @api private | 
					
						
							|  |  |  |   class Upgrade | 
					
						
							|  |  |  |     sig { | 
					
						
							|  |  |  |       params( | 
					
						
							|  |  |  |         casks:               Cask, | 
					
						
							|  |  |  |         args:                Homebrew::CLI::Args, | 
					
						
							|  |  |  |         force:               T.nilable(T::Boolean), | 
					
						
							|  |  |  |         greedy:              T.nilable(T::Boolean), | 
					
						
							|  |  |  |         greedy_latest:       T.nilable(T::Boolean), | 
					
						
							|  |  |  |         greedy_auto_updates: T.nilable(T::Boolean), | 
					
						
							|  |  |  |         dry_run:             T.nilable(T::Boolean), | 
					
						
							|  |  |  |         skip_cask_deps:      T.nilable(T::Boolean), | 
					
						
							|  |  |  |         verbose:             T.nilable(T::Boolean), | 
					
						
							|  |  |  |         binaries:            T.nilable(T::Boolean), | 
					
						
							|  |  |  |         quarantine:          T.nilable(T::Boolean), | 
					
						
							|  |  |  |         require_sha:         T.nilable(T::Boolean), | 
					
						
							|  |  |  |       ).returns(T::Boolean) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     def self.upgrade_casks( | 
					
						
							|  |  |  |       *casks, | 
					
						
							|  |  |  |       args:, | 
					
						
							|  |  |  |       force: false, | 
					
						
							|  |  |  |       greedy: false, | 
					
						
							|  |  |  |       greedy_latest: false, | 
					
						
							|  |  |  |       greedy_auto_updates: false, | 
					
						
							|  |  |  |       dry_run: false, | 
					
						
							|  |  |  |       skip_cask_deps: false, | 
					
						
							|  |  |  |       verbose: false, | 
					
						
							|  |  |  |       binaries: nil, | 
					
						
							|  |  |  |       quarantine: nil, | 
					
						
							|  |  |  |       require_sha: nil | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       quarantine = true if quarantine.nil? | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-05 09:44:32 -04:00
										 |  |  |       greedy = true if Homebrew::EnvConfig.upgrade_greedy? | 
					
						
							| 
									
										
										
										
											2023-04-05 09:28:15 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-07 22:17:02 +09:00
										 |  |  |       outdated_casks = if casks.empty? | 
					
						
							|  |  |  |         Caskroom.casks(config: Config.from_args(args)).select do |cask| | 
					
						
							|  |  |  |           cask.outdated?(greedy: greedy, greedy_latest: greedy_latest, | 
					
						
							|  |  |  |                          greedy_auto_updates: greedy_auto_updates) | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         casks.select do |cask| | 
					
						
							|  |  |  |           raise CaskNotInstalledError, cask if !cask.installed? && !force | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if cask.outdated?(greedy: true) | 
					
						
							|  |  |  |             true | 
					
						
							|  |  |  |           elsif cask.version.latest? | 
					
						
							|  |  |  |             opoo "Not upgrading #{cask.token}, the downloaded artifact has not changed" | 
					
						
							|  |  |  |             false | 
					
						
							|  |  |  |           else | 
					
						
							|  |  |  |             opoo "Not upgrading #{cask.token}, the latest version is already installed" | 
					
						
							|  |  |  |             false | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       manual_installer_casks = outdated_casks.select do |cask| | 
					
						
							|  |  |  |         cask.artifacts.any?(Artifact::Installer::ManualInstaller) | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if manual_installer_casks.present? | 
					
						
							|  |  |  |         count = manual_installer_casks.count | 
					
						
							|  |  |  |         ofail "Not upgrading #{count} `installer manual` #{::Utils.pluralize("cask", count)}." | 
					
						
							|  |  |  |         puts manual_installer_casks.map(&:to_s) | 
					
						
							|  |  |  |         outdated_casks -= manual_installer_casks | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return false if outdated_casks.empty? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if casks.empty? && !greedy | 
					
						
							|  |  |  |         if !greedy_auto_updates && !greedy_latest | 
					
						
							|  |  |  |           ohai "Casks with 'auto_updates true' or 'version :latest' " \ | 
					
						
							|  |  |  |                "will not be upgraded; pass `--greedy` to upgrade them." | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |         if greedy_auto_updates && !greedy_latest | 
					
						
							|  |  |  |           ohai "Casks with 'version :latest' will not be upgraded; pass `--greedy-latest` to upgrade them." | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |         if !greedy_auto_updates && greedy_latest | 
					
						
							|  |  |  |           ohai "Casks with 'auto_updates true' will not be upgraded; pass `--greedy-auto-updates` to upgrade them." | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       verb = dry_run ? "Would upgrade" : "Upgrading" | 
					
						
							|  |  |  |       oh1 "#{verb} #{outdated_casks.count} outdated #{::Utils.pluralize("package", outdated_casks.count)}:" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       caught_exceptions = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       upgradable_casks = outdated_casks.map do |c| | 
					
						
							| 
									
										
										
										
											2023-05-15 11:10:08 +02:00
										 |  |  |         unless c.installed? | 
					
						
							| 
									
										
										
										
											2023-03-07 22:17:02 +09:00
										 |  |  |           odie <<~EOS | 
					
						
							|  |  |  |             The cask '#{c.token}' was affected by a bug and cannot be upgraded as-is. To fix this, run: | 
					
						
							|  |  |  |               brew reinstall --cask --force #{c.token} | 
					
						
							|  |  |  |           EOS | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         [CaskLoader.load(c.installed_caskfile), c] | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       puts upgradable_casks | 
					
						
							|  |  |  |         .map { |(old_cask, new_cask)| "#{new_cask.full_name} #{old_cask.version} -> #{new_cask.version}" } | 
					
						
							|  |  |  |         .join("\n") | 
					
						
							|  |  |  |       return true if dry_run | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       upgradable_casks.each do |(old_cask, new_cask)| | 
					
						
							|  |  |  |         upgrade_cask( | 
					
						
							|  |  |  |           old_cask, new_cask, | 
					
						
							|  |  |  |           binaries: binaries, force: force, skip_cask_deps: skip_cask_deps, verbose: verbose, | 
					
						
							|  |  |  |           quarantine: quarantine, require_sha: require_sha | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |       rescue => e | 
					
						
							| 
									
										
										
										
											2023-05-10 14:46:51 +01:00
										 |  |  |         new_exception = e.exception("#{new_cask.full_name}: #{e}") | 
					
						
							|  |  |  |         new_exception.set_backtrace(e.backtrace) | 
					
						
							|  |  |  |         caught_exceptions << new_exception | 
					
						
							| 
									
										
										
										
											2023-03-07 22:17:02 +09:00
										 |  |  |         next | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return true if caught_exceptions.empty? | 
					
						
							|  |  |  |       raise MultipleCaskErrors, caught_exceptions if caught_exceptions.count > 1
 | 
					
						
							| 
									
										
										
										
											2023-03-18 16:03:10 -07:00
										 |  |  |       raise caught_exceptions.fetch(0) if caught_exceptions.count == 1
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       false | 
					
						
							| 
									
										
										
										
											2023-03-07 22:17:02 +09:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-12 14:47:56 -05:00
										 |  |  |     sig { | 
					
						
							|  |  |  |       params( | 
					
						
							|  |  |  |         old_cask:       Cask, | 
					
						
							|  |  |  |         new_cask:       Cask, | 
					
						
							|  |  |  |         binaries:       T.nilable(T::Boolean), | 
					
						
							|  |  |  |         force:          T.nilable(T::Boolean), | 
					
						
							|  |  |  |         quarantine:     T.nilable(T::Boolean), | 
					
						
							|  |  |  |         require_sha:    T.nilable(T::Boolean), | 
					
						
							|  |  |  |         skip_cask_deps: T.nilable(T::Boolean), | 
					
						
							|  |  |  |         verbose:        T.nilable(T::Boolean), | 
					
						
							|  |  |  |       ).void | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-03-07 22:17:02 +09:00
										 |  |  |     def self.upgrade_cask( | 
					
						
							|  |  |  |       old_cask, new_cask, | 
					
						
							|  |  |  |       binaries:, force:, quarantine:, require_sha:, skip_cask_deps:, verbose: | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |       require "cask/installer" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       start_time = Time.now | 
					
						
							|  |  |  |       odebug "Started upgrade process for Cask #{old_cask}" | 
					
						
							|  |  |  |       old_config = old_cask.config | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       old_options = { | 
					
						
							|  |  |  |         binaries: binaries, | 
					
						
							|  |  |  |         verbose:  verbose, | 
					
						
							|  |  |  |         force:    force, | 
					
						
							|  |  |  |         upgrade:  true, | 
					
						
							|  |  |  |       }.compact | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       old_cask_installer = | 
					
						
							|  |  |  |         Installer.new(old_cask, **old_options) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       new_cask.config = new_cask.default_config.merge(old_config) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       new_options = { | 
					
						
							|  |  |  |         binaries:       binaries, | 
					
						
							|  |  |  |         verbose:        verbose, | 
					
						
							|  |  |  |         force:          force, | 
					
						
							|  |  |  |         skip_cask_deps: skip_cask_deps, | 
					
						
							|  |  |  |         require_sha:    require_sha, | 
					
						
							|  |  |  |         upgrade:        true, | 
					
						
							|  |  |  |         quarantine:     quarantine, | 
					
						
							|  |  |  |       }.compact | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       new_cask_installer = | 
					
						
							|  |  |  |         Installer.new(new_cask, **new_options) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       started_upgrade = false | 
					
						
							|  |  |  |       new_artifacts_installed = false | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       begin | 
					
						
							|  |  |  |         oh1 "Upgrading #{Formatter.identifier(old_cask)}" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Start new cask's installation steps | 
					
						
							|  |  |  |         new_cask_installer.check_conflicts | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (caveats = new_cask_installer.caveats) | 
					
						
							|  |  |  |           puts caveats | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         new_cask_installer.fetch | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Move the old cask's artifacts back to staging | 
					
						
							| 
									
										
										
										
											2023-04-08 16:13:12 -05:00
										 |  |  |         old_cask_installer.start_upgrade(successor: new_cask) | 
					
						
							| 
									
										
										
										
											2023-03-07 22:17:02 +09:00
										 |  |  |         # And flag it so in case of error | 
					
						
							|  |  |  |         started_upgrade = true | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Install the new cask | 
					
						
							|  |  |  |         new_cask_installer.stage | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-08 16:13:12 -05:00
										 |  |  |         new_cask_installer.install_artifacts(predecessor: old_cask) | 
					
						
							| 
									
										
										
										
											2023-03-07 22:17:02 +09:00
										 |  |  |         new_artifacts_installed = true | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-08 14:10:58 +02:00
										 |  |  |         # If successful, wipe the old cask from staging. | 
					
						
							| 
									
										
										
										
											2023-03-07 22:17:02 +09:00
										 |  |  |         old_cask_installer.finalize_upgrade | 
					
						
							|  |  |  |       rescue => e | 
					
						
							| 
									
										
										
										
											2023-04-08 16:13:12 -05:00
										 |  |  |         new_cask_installer.uninstall_artifacts(successor: old_cask) if new_artifacts_installed | 
					
						
							| 
									
										
										
										
											2023-03-07 22:17:02 +09:00
										 |  |  |         new_cask_installer.purge_versioned_files | 
					
						
							| 
									
										
										
										
											2023-04-08 16:13:12 -05:00
										 |  |  |         old_cask_installer.revert_upgrade(predecessor: new_cask) if started_upgrade | 
					
						
							| 
									
										
										
										
											2023-03-07 22:17:02 +09:00
										 |  |  |         raise e | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       end_time = Time.now | 
					
						
							|  |  |  |       Homebrew.messages.package_installed(new_cask.token, end_time - start_time) | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | end |