| 
									
										
										
										
											2025-06-26 23:21:54 +01:00
										 |  |  | # typed: true | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  | # frozen_string_literal: true | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-10 13:04:01 +01:00
										 |  |  | require "English" | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  | require "exceptions" | 
					
						
							|  |  |  | require "extend/ENV" | 
					
						
							|  |  |  | require "utils" | 
					
						
							|  |  |  | require "PATH" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module Homebrew | 
					
						
							|  |  |  |   module Bundle | 
					
						
							|  |  |  |     module Commands | 
					
						
							|  |  |  |       module Exec | 
					
						
							|  |  |  |         PATH_LIKE_ENV_REGEX = /.+#{File::PATH_SEPARATOR}/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-26 23:21:54 +01:00
										 |  |  |         sig { | 
					
						
							|  |  |  |           params( | 
					
						
							|  |  |  |             args:       String, | 
					
						
							|  |  |  |             global:     T::Boolean, | 
					
						
							|  |  |  |             file:       T.nilable(String), | 
					
						
							|  |  |  |             subcommand: String, | 
					
						
							|  |  |  |             services:   T::Boolean, | 
					
						
							|  |  |  |             check:      T::Boolean, | 
					
						
							|  |  |  |           ).void | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-03-28 17:21:28 +08:00
										 |  |  |         def self.run(*args, global: false, file: nil, subcommand: "", services: false, check: false) | 
					
						
							|  |  |  |           if check | 
					
						
							|  |  |  |             require "bundle/commands/check" | 
					
						
							|  |  |  |             Homebrew::Bundle::Commands::Check.run(global:, file:, quiet: true) | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-25 13:47:44 +00:00
										 |  |  |           # Store the old environment so we can check if things were already set | 
					
						
							|  |  |  |           # before we start mutating it. | 
					
						
							|  |  |  |           old_env = ENV.to_h | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  |           # Setup Homebrew's ENV extensions | 
					
						
							|  |  |  |           ENV.activate_extensions! | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           command = args.first | 
					
						
							| 
									
										
										
										
											2025-06-26 23:21:54 +01:00
										 |  |  |           raise UsageError, "No command to execute was specified!" if command.blank? | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-24 21:55:47 +08:00
										 |  |  |           require "bundle/brewfile" | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  |           @dsl = Brewfile.read(global:, file:) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           require "formula" | 
					
						
							|  |  |  |           require "formulary" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-29 08:05:23 -07:00
										 |  |  |           ENV.deps = @dsl.entries.filter_map do |entry| | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  |             next if entry.type != :brew | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             Formulary.factory(entry.name) | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           # Allow setting all dependencies to be keg-only | 
					
						
							|  |  |  |           # (i.e. should be explicitly in HOMEBREW_*PATHs ahead of HOMEBREW_PREFIX) | 
					
						
							| 
									
										
										
										
											2025-06-29 08:05:23 -07:00
										 |  |  |           ENV.keg_only_deps = if ENV["HOMEBREW_BUNDLE_EXEC_ALL_KEG_ONLY_DEPS"].present? | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  |             ENV.delete("HOMEBREW_BUNDLE_EXEC_ALL_KEG_ONLY_DEPS") | 
					
						
							| 
									
										
										
										
											2025-06-29 08:05:23 -07:00
										 |  |  |             ENV.deps | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  |           else | 
					
						
							| 
									
										
										
										
											2025-06-29 08:05:23 -07:00
										 |  |  |             ENV.deps.select(&:keg_only?) | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  |           end | 
					
						
							| 
									
										
										
										
											2025-06-29 08:05:23 -07:00
										 |  |  |           ENV.setup_build_environment | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |           # Enable compiler flag filtering | 
					
						
							|  |  |  |           ENV.refurbish_args | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           # Set up `nodenv`, `pyenv` and `rbenv` if present. | 
					
						
							|  |  |  |           env_formulae = %w[nodenv pyenv rbenv] | 
					
						
							| 
									
										
										
										
											2025-06-29 08:05:23 -07:00
										 |  |  |           ENV.deps.each do |dep| | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  |             dep_name = dep.name | 
					
						
							|  |  |  |             next unless env_formulae.include?(dep_name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             dep_root = ENV.fetch("HOMEBREW_#{dep_name.upcase}_ROOT", "#{Dir.home}/.#{dep_name}") | 
					
						
							|  |  |  |             ENV.prepend_path "PATH", Pathname.new(dep_root)/"shims" | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-09 19:06:16 +01:00
										 |  |  |           # Setup pkgconf, if needed, to help locate packages | 
					
						
							|  |  |  |           Bundle.prepend_pkgconf_path_if_needed! | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-19 21:27:45 +08:00
										 |  |  |           # For commands which aren't either absolute or relative | 
					
						
							|  |  |  |           # Add the command directory to PATH, since it may get blown away by superenv | 
					
						
							|  |  |  |           if command.exclude?("/") && (which_command = which(command)).present? | 
					
						
							|  |  |  |             ENV.prepend_path "PATH", which_command.dirname.to_s | 
					
						
							|  |  |  |           end | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |           # Replace the formula versions from the environment variables | 
					
						
							| 
									
										
										
										
											2025-06-29 08:05:23 -07:00
										 |  |  |           ENV.deps.each do |formula| | 
					
						
							| 
									
										
										
										
											2025-04-03 11:05:07 +01:00
										 |  |  |             formula_name = formula.name | 
					
						
							|  |  |  |             formula_version = Bundle.formula_versions_from_env(formula_name) | 
					
						
							|  |  |  |             next unless formula_version | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  |             ENV.each do |key, value| | 
					
						
							|  |  |  |               opt = %r{/opt/#{formula_name}([/:$])} | 
					
						
							|  |  |  |               next unless value.match(opt) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               cellar = "/Cellar/#{formula_name}/#{formula_version}\\1" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               # Look for PATH-like environment variables | 
					
						
							|  |  |  |               ENV[key] = if key.include?("PATH") && value.match?(PATH_LIKE_ENV_REGEX) | 
					
						
							|  |  |  |                 rejected_opts = [] | 
					
						
							|  |  |  |                 path = PATH.new(ENV.fetch("PATH")) | 
					
						
							| 
									
										
										
										
											2025-03-26 11:00:11 -07:00
										 |  |  |                            .reject do |path_value| | 
					
						
							| 
									
										
										
										
											2025-06-29 08:05:23 -07:00
										 |  |  |                   rejected_opts << path_value if path_value.match?(opt) | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  |                 end | 
					
						
							| 
									
										
										
										
											2025-03-26 11:00:11 -07:00
										 |  |  |                 rejected_opts.each do |path_value| | 
					
						
							|  |  |  |                   path.prepend(path_value.gsub(opt, cellar)) | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  |                 end | 
					
						
							|  |  |  |                 path.to_s | 
					
						
							|  |  |  |               else | 
					
						
							|  |  |  |                 value.gsub(opt, cellar) | 
					
						
							|  |  |  |               end | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-25 11:53:23 +01:00
										 |  |  |           # Ensure brew bundle exec/sh/env commands have access to other tools in the PATH | 
					
						
							|  |  |  |           if (homebrew_path = ENV.fetch("HOMEBREW_PATH", nil)) | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  |             ENV.append_path "PATH", homebrew_path | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-19 21:27:45 +08:00
										 |  |  |           # For commands which aren't either absolute or relative | 
					
						
							|  |  |  |           raise "command was not found in your PATH: #{command}" if command.exclude?("/") && which(command).nil? | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-14 19:10:58 +01:00
										 |  |  |           %w[HOMEBREW_TEMP TMPDIR HOMEBREW_TMPDIR].each do |var| | 
					
						
							|  |  |  |             value = ENV.fetch(var, nil) | 
					
						
							|  |  |  |             next if value.blank? | 
					
						
							|  |  |  |             next if File.writable?(value) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             ENV.delete(var) | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-25 11:53:23 +01:00
										 |  |  |           ENV.each do |key, value| | 
					
						
							|  |  |  |             # Look for PATH-like environment variables | 
					
						
							|  |  |  |             next if key.exclude?("PATH") || !value.match?(PATH_LIKE_ENV_REGEX) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # Exclude Homebrew shims from the PATH as they don't work | 
					
						
							|  |  |  |             # without all Homebrew environment variables and can interfere with | 
					
						
							|  |  |  |             # non-Homebrew builds. | 
					
						
							|  |  |  |             ENV[key] = PATH.new(value) | 
					
						
							|  |  |  |                            .reject do |path_value| | 
					
						
							|  |  |  |               path_value.include?("/Homebrew/shims/") | 
					
						
							|  |  |  |             end.to_s | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  |           if subcommand == "env" | 
					
						
							| 
									
										
										
										
											2025-03-25 13:47:44 +00:00
										 |  |  |             ENV.sort.each do |key, value| | 
					
						
							| 
									
										
										
										
											2025-04-10 16:16:05 +01:00
										 |  |  |               # Skip exporting Homebrew internal variables that won't be used by other tools. | 
					
						
							|  |  |  |               # Those Homebrew needs have already been set to global constants and/or are exported again later. | 
					
						
							|  |  |  |               # Setting these globally can interfere with nested Homebrew invocations/environments. | 
					
						
							| 
									
										
										
										
											2025-04-25 11:53:23 +01:00
										 |  |  |               if key.start_with?("HOMEBREW_", "PORTABLE_RUBY_") | 
					
						
							|  |  |  |                 ENV.delete(key) | 
					
						
							|  |  |  |                 next | 
					
						
							|  |  |  |               end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               # No need to export empty values. | 
					
						
							|  |  |  |               next if value.blank? | 
					
						
							| 
									
										
										
										
											2025-04-10 16:16:05 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-10 15:23:02 +01:00
										 |  |  |               # Skip exporting things that were the same in the old environment. | 
					
						
							|  |  |  |               old_value = old_env[key] | 
					
						
							|  |  |  |               next if old_value == value | 
					
						
							| 
									
										
										
										
											2025-03-25 13:47:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-10 15:23:02 +01:00
										 |  |  |               # Look for PATH-like environment variables | 
					
						
							|  |  |  |               if key.include?("PATH") && value.match?(PATH_LIKE_ENV_REGEX) | 
					
						
							|  |  |  |                 old_values = old_value.to_s.split(File::PATH_SEPARATOR) | 
					
						
							|  |  |  |                 path = PATH.new(value) | 
					
						
							|  |  |  |                            .reject do |path_value| | 
					
						
							|  |  |  |                   # Exclude existing/old values as they've already been exported. | 
					
						
							| 
									
										
										
										
											2025-04-25 11:53:23 +01:00
										 |  |  |                   old_values.include?(path_value) | 
					
						
							| 
									
										
										
										
											2025-04-10 15:23:02 +01:00
										 |  |  |                 end | 
					
						
							|  |  |  |                 next if path.blank? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 puts "export #{key}=\"#{Utils::Shell.sh_quote(path.to_s)}:${#{key}:-}\"" | 
					
						
							|  |  |  |               else | 
					
						
							|  |  |  |                 puts "export #{key}=\"#{Utils::Shell.sh_quote(value)}\"" | 
					
						
							|  |  |  |               end | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  |             end | 
					
						
							|  |  |  |             return | 
					
						
							| 
									
										
										
										
											2025-06-05 15:43:34 +01:00
										 |  |  |           elsif subcommand == "sh" | 
					
						
							|  |  |  |             preferred_path = Utils::Shell.preferred_path(default: "/bin/bash") | 
					
						
							|  |  |  |             notice = unless Homebrew::EnvConfig.no_env_hints? | 
					
						
							|  |  |  |               <<~EOS | 
					
						
							|  |  |  |                 Your shell has been configured to use a build environment from your `Brewfile`. | 
					
						
							|  |  |  |                 This should help you build stuff. | 
					
						
							|  |  |  |                 Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`). | 
					
						
							|  |  |  |                 When done, type `exit`. | 
					
						
							|  |  |  |               EOS | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  |             ENV["HOMEBREW_FORCE_API_AUTO_UPDATE"] = nil | 
					
						
							|  |  |  |             args = [Utils::Shell.shell_with_prompt("brew bundle", preferred_path:, notice:)] | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  |           end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-27 06:12:10 +00:00
										 |  |  |           if services | 
					
						
							| 
									
										
										
										
											2025-03-28 05:39:46 +00:00
										 |  |  |             require "bundle/brew_services" | 
					
						
							| 
									
										
										
										
											2025-03-27 06:12:10 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-26 23:21:54 +01:00
										 |  |  |             exit_code = T.let(0, Integer) | 
					
						
							| 
									
										
										
										
											2025-03-28 05:39:46 +00:00
										 |  |  |             run_services(@dsl.entries) do | 
					
						
							| 
									
										
										
										
											2025-03-27 06:12:10 +00:00
										 |  |  |               Kernel.system(*args) | 
					
						
							| 
									
										
										
										
											2025-05-21 16:31:30 +01:00
										 |  |  |               if (system_exit_code = $CHILD_STATUS&.exitstatus) | 
					
						
							|  |  |  |                 exit_code = system_exit_code | 
					
						
							|  |  |  |               end | 
					
						
							| 
									
										
										
										
											2025-03-27 06:12:10 +00:00
										 |  |  |             end | 
					
						
							|  |  |  |             exit!(exit_code) | 
					
						
							|  |  |  |           else | 
					
						
							|  |  |  |             exec(*args) | 
					
						
							|  |  |  |           end | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  |         end | 
					
						
							| 
									
										
										
										
											2025-03-28 05:39:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         sig { | 
					
						
							|  |  |  |           params( | 
					
						
							|  |  |  |             entries: T::Array[Homebrew::Bundle::Dsl::Entry], | 
					
						
							|  |  |  |             _block:  T.proc.params( | 
					
						
							| 
									
										
										
										
											2025-04-07 14:10:42 +01:00
										 |  |  |               entry:                Homebrew::Bundle::Dsl::Entry, | 
					
						
							| 
									
										
										
										
											2025-06-26 23:21:54 +01:00
										 |  |  |               info:                 T::Hash[String, T.untyped], | 
					
						
							| 
									
										
										
										
											2025-03-28 05:39:46 +00:00
										 |  |  |               service_file:         Pathname, | 
					
						
							| 
									
										
										
										
											2025-06-29 08:05:23 -07:00
										 |  |  |               conflicting_services: T::Array[T::Hash[String, T.anything]], | 
					
						
							| 
									
										
										
										
											2025-03-28 05:39:46 +00:00
										 |  |  |             ).void, | 
					
						
							|  |  |  |           ).void | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         private_class_method def self.map_service_info(entries, &_block) | 
					
						
							|  |  |  |           entries_formulae = entries.filter_map do |entry| | 
					
						
							|  |  |  |             next if entry.type != :brew | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             formula = Formula[entry.name] | 
					
						
							|  |  |  |             next unless formula.any_version_installed? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             [entry, formula] | 
					
						
							|  |  |  |           end.to_h | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-07 14:12:41 +01:00
										 |  |  |           return if entries_formulae.empty? | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-02 07:30:14 +01:00
										 |  |  |           conflicts = entries_formulae.to_h do |entry, formula| | 
					
						
							|  |  |  |             [ | 
					
						
							|  |  |  |               entry, | 
					
						
							|  |  |  |               ( | 
					
						
							|  |  |  |                 formula.versioned_formulae_names + | 
					
						
							|  |  |  |                   formula.conflicts.map(&:name) + | 
					
						
							|  |  |  |                   Array(entry.options[:conflicts_with]) | 
					
						
							|  |  |  |               ).uniq, | 
					
						
							|  |  |  |             ] | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-28 05:39:46 +00:00
										 |  |  |           # The formula + everything that could possible conflict with the service | 
					
						
							|  |  |  |           names_to_query = entries_formulae.flat_map do |entry, formula| | 
					
						
							|  |  |  |             [ | 
					
						
							|  |  |  |               formula.name, | 
					
						
							| 
									
										
										
										
											2025-04-02 07:30:14 +01:00
										 |  |  |               *conflicts.fetch(entry), | 
					
						
							| 
									
										
										
										
											2025-03-28 05:39:46 +00:00
										 |  |  |             ] | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           # We parse from a command invocation so that brew wrappers can invoke special actions | 
					
						
							|  |  |  |           # for the elevated nature of `brew services` | 
					
						
							|  |  |  |           services_info = JSON.parse( | 
					
						
							|  |  |  |             Utils.safe_popen_read(HOMEBREW_BREW_FILE, "services", "info", "--json", *names_to_query), | 
					
						
							|  |  |  |           ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           entries_formulae.filter_map do |entry, formula| | 
					
						
							|  |  |  |             service_file = Bundle::BrewServices.versioned_service_file(entry.name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             unless service_file&.file? | 
					
						
							|  |  |  |               prefix = formula.any_installed_prefix | 
					
						
							|  |  |  |               next if prefix.nil? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               service_file = if Homebrew::Services::System.launchctl? | 
					
						
							|  |  |  |                 prefix/"#{formula.plist_name}.plist" | 
					
						
							|  |  |  |               else | 
					
						
							|  |  |  |                 prefix/"#{formula.service_name}.service" | 
					
						
							|  |  |  |               end | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             next unless service_file.file? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             info = services_info.find { |candidate| candidate["name"] == formula.name } | 
					
						
							|  |  |  |             conflicting_services = services_info.select do |candidate| | 
					
						
							|  |  |  |               next unless candidate["running"] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-02 07:30:14 +01:00
										 |  |  |               conflicts.fetch(entry).include?(candidate["name"]) | 
					
						
							| 
									
										
										
										
											2025-03-28 05:39:46 +00:00
										 |  |  |             end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             raise "Failed to get service info for #{entry.name}" if info.nil? | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-07 14:10:42 +01:00
										 |  |  |             yield entry, info, service_file, conflicting_services | 
					
						
							| 
									
										
										
										
											2025-03-28 05:39:46 +00:00
										 |  |  |           end | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         sig { params(entries: T::Array[Homebrew::Bundle::Dsl::Entry], _block: T.nilable(T.proc.void)).void } | 
					
						
							|  |  |  |         private_class_method def self.run_services(entries, &_block) | 
					
						
							| 
									
										
										
										
											2025-04-07 14:10:42 +01:00
										 |  |  |           entries_to_stop = [] | 
					
						
							| 
									
										
										
										
											2025-03-28 05:39:46 +00:00
										 |  |  |           services_to_restart = [] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-07 14:10:42 +01:00
										 |  |  |           map_service_info(entries) do |entry, info, service_file, conflicting_services| | 
					
						
							|  |  |  |             # Don't restart if already running this version | 
					
						
							|  |  |  |             loaded_file = Pathname.new(info["loaded_file"].to_s) | 
					
						
							| 
									
										
										
										
											2025-06-26 23:21:54 +01:00
										 |  |  |             next if info["running"] && loaded_file.file? && loaded_file.realpath == service_file.realpath | 
					
						
							| 
									
										
										
										
											2025-04-07 14:10:42 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-28 05:39:46 +00:00
										 |  |  |             if info["running"] && !Bundle::BrewServices.stop(info["name"], keep: true) | 
					
						
							|  |  |  |               opoo "Failed to stop #{info["name"]} service" | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             conflicting_services.each do |conflict| | 
					
						
							|  |  |  |               if Bundle::BrewServices.stop(conflict["name"], keep: true) | 
					
						
							|  |  |  |                 services_to_restart << conflict["name"] if conflict["registered"] | 
					
						
							|  |  |  |               else | 
					
						
							|  |  |  |                 opoo "Failed to stop #{conflict["name"]} service" | 
					
						
							|  |  |  |               end | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             unless Bundle::BrewServices.run(info["name"], file: service_file) | 
					
						
							|  |  |  |               opoo "Failed to start #{info["name"]} service" | 
					
						
							|  |  |  |             end | 
					
						
							| 
									
										
										
										
											2025-04-07 14:10:42 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |             entries_to_stop << entry | 
					
						
							| 
									
										
										
										
											2025-03-28 05:39:46 +00:00
										 |  |  |           end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           return unless block_given? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           begin | 
					
						
							|  |  |  |             yield | 
					
						
							|  |  |  |           ensure | 
					
						
							|  |  |  |             # Do a full re-evaluation of services instead state has changed | 
					
						
							| 
									
										
										
										
											2025-04-07 14:10:42 +01:00
										 |  |  |             stop_services(entries_to_stop) | 
					
						
							| 
									
										
										
										
											2025-03-28 05:39:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |             services_to_restart.each do |service| | 
					
						
							|  |  |  |               next if Bundle::BrewServices.run(service) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               opoo "Failed to restart #{service} service" | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         sig { params(entries: T::Array[Homebrew::Bundle::Dsl::Entry]).void } | 
					
						
							|  |  |  |         private_class_method def self.stop_services(entries) | 
					
						
							| 
									
										
										
										
											2025-04-07 14:10:42 +01:00
										 |  |  |           map_service_info(entries) do |_, info, _, _| | 
					
						
							| 
									
										
										
										
											2025-03-28 05:39:46 +00:00
										 |  |  |             next unless info["loaded"] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # Try avoid services not started by `brew bundle services` | 
					
						
							|  |  |  |             next if Homebrew::Services::System.launchctl? && info["registered"] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if info["running"] && !Bundle::BrewServices.stop(info["name"], keep: true) | 
					
						
							|  |  |  |               opoo "Failed to stop #{info["name"]} service" | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |         end | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | end |