Fold brew bundle services into exec
				
					
				
			This commit is contained in:
		
							parent
							
								
									c7e8b66da3
								
							
						
					
					
						commit
						650f62bcba
					
				@ -158,10 +158,10 @@ module Homebrew
 | 
			
		||||
          end
 | 
			
		||||
 | 
			
		||||
          if services
 | 
			
		||||
            require "bundle/commands/services"
 | 
			
		||||
            require "bundle/brew_services"
 | 
			
		||||
 | 
			
		||||
            exit_code = 0
 | 
			
		||||
            Services.run_services(@dsl.entries) do
 | 
			
		||||
            run_services(@dsl.entries) do
 | 
			
		||||
              Kernel.system(*args)
 | 
			
		||||
              exit_code = $CHILD_STATUS.exitstatus
 | 
			
		||||
            end
 | 
			
		||||
@ -170,6 +170,123 @@ module Homebrew
 | 
			
		||||
            exec(*args)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        sig {
 | 
			
		||||
          params(
 | 
			
		||||
            entries: T::Array[Homebrew::Bundle::Dsl::Entry],
 | 
			
		||||
            _block:  T.proc.params(
 | 
			
		||||
              info:                 T::Hash[String, T.anything],
 | 
			
		||||
              service_file:         Pathname,
 | 
			
		||||
              conflicting_services: T::Array[T::Hash[String, T.anything]],
 | 
			
		||||
            ).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
 | 
			
		||||
 | 
			
		||||
          # The formula + everything that could possible conflict with the service
 | 
			
		||||
          names_to_query = entries_formulae.flat_map do |entry, formula|
 | 
			
		||||
            [
 | 
			
		||||
              formula.name,
 | 
			
		||||
              *formula.versioned_formulae_names,
 | 
			
		||||
              *formula.conflicts.map(&:name),
 | 
			
		||||
              *entry.options[:conflicts_with],
 | 
			
		||||
            ]
 | 
			
		||||
          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"]
 | 
			
		||||
 | 
			
		||||
              formula.versioned_formulae_names.include?(candidate["name"])
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
            raise "Failed to get service info for #{entry.name}" if info.nil?
 | 
			
		||||
 | 
			
		||||
            yield info, service_file, conflicting_services
 | 
			
		||||
          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)
 | 
			
		||||
          services_to_restart = []
 | 
			
		||||
 | 
			
		||||
          map_service_info(entries) do |info, service_file, conflicting_services|
 | 
			
		||||
            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
 | 
			
		||||
          end
 | 
			
		||||
 | 
			
		||||
          return unless block_given?
 | 
			
		||||
 | 
			
		||||
          begin
 | 
			
		||||
            yield
 | 
			
		||||
          ensure
 | 
			
		||||
            # Do a full re-evaluation of services instead state has changed
 | 
			
		||||
            stop_services(entries)
 | 
			
		||||
 | 
			
		||||
            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)
 | 
			
		||||
          map_service_info(entries) do |info, _, _|
 | 
			
		||||
            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
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@ -1,143 +0,0 @@
 | 
			
		||||
# typed: strict
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
require "bundle/brewfile"
 | 
			
		||||
require "bundle/brew_services"
 | 
			
		||||
require "formula"
 | 
			
		||||
 | 
			
		||||
module Homebrew
 | 
			
		||||
  module Bundle
 | 
			
		||||
    module Commands
 | 
			
		||||
      module Services
 | 
			
		||||
        sig { params(args: String, global: T::Boolean, file: T.nilable(String)).void }
 | 
			
		||||
        def self.run(*args, global:, file:)
 | 
			
		||||
          raise UsageError, "invalid `brew bundle services` arguments" if args.length != 1
 | 
			
		||||
 | 
			
		||||
          parsed_entries = Brewfile.read(global:, file:).entries
 | 
			
		||||
 | 
			
		||||
          subcommand = args.first
 | 
			
		||||
          case subcommand
 | 
			
		||||
          when "run"
 | 
			
		||||
            run_services(parsed_entries)
 | 
			
		||||
          when "stop"
 | 
			
		||||
            stop_services(parsed_entries)
 | 
			
		||||
          else
 | 
			
		||||
            raise UsageError, "unknown bundle services subcommand: #{subcommand}"
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        sig {
 | 
			
		||||
          params(
 | 
			
		||||
            entries: T::Array[Homebrew::Bundle::Dsl::Entry],
 | 
			
		||||
            _block:  T.proc.params(
 | 
			
		||||
              info:                 T::Hash[String, T.anything],
 | 
			
		||||
              service_file:         Pathname,
 | 
			
		||||
              conflicting_services: T::Array[T::Hash[String, T.anything]],
 | 
			
		||||
            ).void,
 | 
			
		||||
          ).void
 | 
			
		||||
        }
 | 
			
		||||
        private_class_method def self.map_entries(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
 | 
			
		||||
 | 
			
		||||
          # The formula + everything that could possible conflict with the service
 | 
			
		||||
          names_to_query = entries_formulae.flat_map do |entry, formula|
 | 
			
		||||
            [
 | 
			
		||||
              formula.name,
 | 
			
		||||
              *formula.versioned_formulae_names,
 | 
			
		||||
              *formula.conflicts.map(&:name),
 | 
			
		||||
              *entry.options[:conflicts_with],
 | 
			
		||||
            ]
 | 
			
		||||
          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"]
 | 
			
		||||
 | 
			
		||||
              formula.versioned_formulae_names.include?(candidate["name"])
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
            raise "Failed to get service info for #{entry.name}" if info.nil?
 | 
			
		||||
 | 
			
		||||
            yield info, service_file, conflicting_services
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        sig { params(entries: T::Array[Homebrew::Bundle::Dsl::Entry], _block: T.nilable(T.proc.void)).void }
 | 
			
		||||
        def self.run_services(entries, &_block)
 | 
			
		||||
          map_entries(entries) do |info, service_file, conflicting_services|
 | 
			
		||||
            if info["running"] && !Bundle::BrewServices.stop(info["name"], keep: true)
 | 
			
		||||
              opoo "Failed to stop #{info["name"]} service"
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
            conflicting_services.each do |conflict|
 | 
			
		||||
              if conflict["running"] && !Bundle::BrewServices.stop(conflict["name"], keep: true)
 | 
			
		||||
                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
 | 
			
		||||
 | 
			
		||||
            return unless block_given?
 | 
			
		||||
 | 
			
		||||
            begin
 | 
			
		||||
              yield
 | 
			
		||||
            ensure
 | 
			
		||||
              stop_services(entries)
 | 
			
		||||
 | 
			
		||||
              conflicting_services.each do |conflict|
 | 
			
		||||
                if conflict["running"] && conflict["registered"] && !Bundle::BrewServices.run(conflict["name"])
 | 
			
		||||
                  opoo "Failed to restart #{conflict["name"]} service"
 | 
			
		||||
                end
 | 
			
		||||
              end
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        sig { params(entries: T::Array[Homebrew::Bundle::Dsl::Entry]).void }
 | 
			
		||||
        def self.stop_services(entries)
 | 
			
		||||
          map_entries(entries) do |info, _, _|
 | 
			
		||||
            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
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -61,12 +61,6 @@ module Homebrew
 | 
			
		||||
 | 
			
		||||
          `brew bundle env`:
 | 
			
		||||
          Print the environment variables that would be set in a `brew bundle exec` environment.
 | 
			
		||||
 | 
			
		||||
          `brew bundle services run`:
 | 
			
		||||
          Start services for formulae specified in the `Brewfile`.
 | 
			
		||||
 | 
			
		||||
          `brew bundle services stop`:
 | 
			
		||||
          Stop services for formulae specified in the `Brewfile`.
 | 
			
		||||
        EOS
 | 
			
		||||
        flag "--file=",
 | 
			
		||||
             description: "Read from or write to the `Brewfile` from this location. " \
 | 
			
		||||
@ -141,7 +135,7 @@ module Homebrew
 | 
			
		||||
        require "bundle"
 | 
			
		||||
 | 
			
		||||
        subcommand = args.named.first.presence
 | 
			
		||||
        if %w[exec add remove services].exclude?(subcommand) && args.named.size > 1
 | 
			
		||||
        if %w[exec add remove].exclude?(subcommand) && args.named.size > 1
 | 
			
		||||
          raise UsageError, "This command does not take more than 1 subcommand argument."
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
@ -281,10 +275,6 @@ module Homebrew
 | 
			
		||||
            require "bundle/commands/remove"
 | 
			
		||||
            Homebrew::Bundle::Commands::Remove.run(*named_args, type: selected_types.first, global:, file:)
 | 
			
		||||
          end
 | 
			
		||||
        when "services"
 | 
			
		||||
          _, *named_args = args.named
 | 
			
		||||
          require "bundle/commands/services"
 | 
			
		||||
          Homebrew::Bundle::Commands::Services.run(*named_args, global:, file:)
 | 
			
		||||
        else
 | 
			
		||||
          raise UsageError, "unknown subcommand: #{subcommand}"
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user