Skip to content

Commit 4b126ef

Browse files
committed
Add brew bundle services helper
1 parent fd0c3c0 commit 4b126ef

File tree

6 files changed

+133
-1
lines changed

6 files changed

+133
-1
lines changed

Library/Homebrew/bundle.rb

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
require "bundle/dumper"
2626
require "bundle/installer"
2727
require "bundle/lister"
28+
require "bundle/services"
2829
require "bundle/commands/install"
2930
require "bundle/commands/dump"
3031
require "bundle/commands/cleanup"
@@ -33,6 +34,7 @@
3334
require "bundle/commands/list"
3435
require "bundle/commands/add"
3536
require "bundle/commands/remove"
37+
require "bundle/commands/services"
3638
require "bundle/whalebrew_installer"
3739
require "bundle/whalebrew_dumper"
3840
require "bundle/vscode_extension_checker"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# typed: strict
2+
# frozen_string_literal: true
3+
4+
module Homebrew
5+
module Bundle
6+
module Commands
7+
module Services
8+
sig { params(args: String, global: T::Boolean, file: T.nilable(String)).void }
9+
def self.run(*args, global:, file:)
10+
raise UsageError, "invalid `brew bundle services` arguments" if args.length != 1
11+
12+
parsed_entries = Brewfile.read(global:, file:).entries
13+
14+
subcommand = args.first
15+
case subcommand
16+
when "run"
17+
Homebrew::Bundle::Services.run(parsed_entries)
18+
when "stop"
19+
Homebrew::Bundle::Services.stop(parsed_entries)
20+
else
21+
raise UsageError, "unknown services subcommand: #{subcommand}"
22+
end
23+
end
24+
end
25+
end
26+
end
27+
end

Library/Homebrew/bundle/services.rb

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# typed: strict
2+
# frozen_string_literal: true
3+
4+
require "formula"
5+
require "services/system"
6+
7+
module Homebrew
8+
module Bundle
9+
module Services
10+
sig {
11+
params(
12+
entries: T::Array[Homebrew::Bundle::Dsl::Entry],
13+
_block: T.proc.params(info: T::Hash[String, T.anything], service_file: Pathname).void,
14+
).void
15+
}
16+
private_class_method def self.map_entries(entries, &_block)
17+
formula_versions = {}
18+
ENV.each do |key, value|
19+
match = key.match(/^HOMEBREW_BUNDLE_EXEC_FORMULA_VERSION_(.+)$/)
20+
next if match.blank?
21+
22+
formula_name = match[1]
23+
next if formula_name.blank?
24+
25+
ENV.delete(key)
26+
formula_versions[formula_name.downcase] = value
27+
end
28+
29+
entries.filter_map do |entry|
30+
next if entry.type != :brew
31+
32+
formula = Formula[entry.name]
33+
next unless formula.any_version_installed?
34+
35+
version = formula_versions[entry.name.downcase]
36+
prefix = formula.rack/version if version
37+
38+
service_file = if prefix&.directory?
39+
if Homebrew::Services::System.launchctl?
40+
prefix/"#{formula.plist_name}.plist"
41+
else
42+
prefix/"#{formula.service_name}.service"
43+
end
44+
end
45+
46+
unless service_file&.file?
47+
prefix = formula.any_installed_prefix
48+
next if prefix.nil?
49+
50+
service_file = if Homebrew::Services::System.launchctl?
51+
prefix/"#{formula.plist_name}.plist"
52+
else
53+
prefix/"#{formula.service_name}.service"
54+
end
55+
end
56+
57+
next unless service_file.file?
58+
59+
# We parse from a command invocation so that brew wrappers can invoke special actions
60+
# for the elevated nature of `brew services`
61+
output = Utils.safe_popen_read(HOMEBREW_BREW_FILE, "services", "info", "--json", formula.full_name)
62+
info = JSON.parse(output)
63+
64+
raise "Failed to get service info for #{entry.name}" if info.length != 1
65+
66+
yield info.first, service_file
67+
end
68+
end
69+
70+
sig { params(entries: T::Array[Homebrew::Bundle::Dsl::Entry]).void }
71+
def self.run(entries)
72+
map_entries(entries) do |info, service_file|
73+
next if info["running"]
74+
75+
safe_system HOMEBREW_BREW_FILE, "services", "run", "--file=#{service_file}", info["name"]
76+
end
77+
end
78+
79+
sig { params(entries: T::Array[Homebrew::Bundle::Dsl::Entry]).void }
80+
def self.stop(entries)
81+
map_entries(entries) do |info, _service_file|
82+
next unless info["loaded"]
83+
84+
# Try avoid services not started by `brew bundle services`
85+
next if Homebrew::Services::System.launchctl? && info["registered"]
86+
87+
safe_system HOMEBREW_BREW_FILE, "services", "stop", info["name"]
88+
end
89+
end
90+
end
91+
end
92+
end

Library/Homebrew/cmd/bundle.rb

+10-1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ class Bundle < AbstractCommand
6161
6262
`brew bundle env`:
6363
Print the environment variables that would be set in a `brew bundle exec` environment.
64+
65+
`brew bundle services run`:
66+
Start services for formulae specified in the `Brewfile`.
67+
68+
`brew bundle services stop`:
69+
Stop services for formulae specified in the `Brewfile`.
6470
EOS
6571
flag "--file=",
6672
description: "Read from or write to the `Brewfile` from this location. " \
@@ -133,7 +139,7 @@ def run
133139
require "bundle"
134140

135141
subcommand = args.named.first.presence
136-
if ["exec", "add", "remove"].exclude?(subcommand) && args.named.size > 1
142+
if %w[exec add remove services].exclude?(subcommand) && args.named.size > 1
137143
raise UsageError, "This command does not take more than 1 subcommand argument."
138144
end
139145

@@ -263,6 +269,9 @@ def run
263269
else
264270
Homebrew::Bundle::Commands::Remove.run(*named_args, type: selected_types.first, global:, file:)
265271
end
272+
when "services"
273+
_, *named_args = args.named
274+
Homebrew::Bundle::Commands::Services.run(*named_args, global:, file:)
266275
else
267276
raise UsageError, "unknown subcommand: #{subcommand}"
268277
end

Library/Homebrew/services/commands/info.rb

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ def self.output(hash, verbose:)
5353
return out unless verbose
5454

5555
out += "File: #{hash[:file]} #{pretty_bool(hash[:file].present?)}\n"
56+
out += "Registered at login: #{pretty_bool(hash[:registered])}\n"
5657
out += "Command: #{hash[:command]}\n" unless hash[:command].nil?
5758
out += "Working directory: #{hash[:working_dir]}\n" unless hash[:working_dir].nil?
5859
out += "Root directory: #{hash[:root_dir]}\n" unless hash[:root_dir].nil?

Library/Homebrew/services/formula_wrapper.rb

+1
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ def to_hash
201201
user: owner,
202202
status: status_symbol,
203203
file: service_file_present? ? dest : service_file,
204+
registered: service_file_present?,
204205
}
205206

206207
return hash unless service?

0 commit comments

Comments
 (0)