-
-
Notifications
You must be signed in to change notification settings - Fork 10.3k
Add brew bundle exec --services
#19552
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0b58e8f
615fb76
2b906e4
786ad34
c7e8b66
650f62b
c273d8b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -47,7 +47,7 @@ | |
|
||
PATH_LIKE_ENV_REGEX = /.+#{File::PATH_SEPARATOR}/ | ||
|
||
def self.run(*args, global: false, file: nil, subcommand: "") | ||
def self.run(*args, global: false, file: nil, subcommand: "", services: false) | ||
# Cleanup Homebrew's global environment | ||
HOMEBREW_ENV_CLEANUP.each { |key| ENV.delete(key) } | ||
|
||
|
@@ -157,7 +157,135 @@ | |
return | ||
end | ||
|
||
exec(*args) | ||
if services | ||
require "bundle/brew_services" | ||
|
||
exit_code = 0 | ||
run_services(@dsl.entries) do | ||
Kernel.system(*args) | ||
exit_code = $CHILD_STATUS.exitstatus | ||
end | ||
exit!(exit_code) | ||
else | ||
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| | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would love a follow-up PR with some test coverage here. |
||
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 = [] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would love a follow-up PR with some test coverage here. |
||
|
||
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, _, _| | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would love a follow-up PR with some test coverage here. |
||
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 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would love a follow-up PR with some test coverage here.