Skip to content
This repository was archived by the owner on Apr 22, 2025. It is now read-only.

Commit 62c8ae8

Browse files
committed
Add brew bundle services
1 parent f3b381d commit 62c8ae8

File tree

4 files changed

+124
-1
lines changed

4 files changed

+124
-1
lines changed

cmd/bundle.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ def run
143143
require_relative "../lib/bundle"
144144

145145
subcommand = args.named.first.presence
146-
if ["exec", "add", "remove"].exclude?(subcommand) && args.named.size > 1
146+
if %w[exec add remove services].exclude?(subcommand) && args.named.size > 1
147147
raise UsageError, "This command does not take more than 1 subcommand argument."
148148
end
149149

@@ -273,6 +273,9 @@ def run
273273
else
274274
Bundle::Commands::Remove.run(*named_args, type: selected_types.first, global:, file:)
275275
end
276+
when "services"
277+
_, *named_args = args.named
278+
Bundle::Commands::Services.run(*named_args, global:, file:)
276279
else
277280
raise UsageError, "unknown subcommand: #{subcommand}"
278281
end

lib/bundle.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
require "bundle/dumper"
2828
require "bundle/installer"
2929
require "bundle/lister"
30+
require "bundle/services"
3031
require "bundle/commands/install"
3132
require "bundle/commands/dump"
3233
require "bundle/commands/cleanup"
@@ -35,6 +36,7 @@
3536
require "bundle/commands/list"
3637
require "bundle/commands/add"
3738
require "bundle/commands/remove"
39+
require "bundle/commands/services"
3840
require "bundle/whalebrew_installer"
3941
require "bundle/whalebrew_dumper"
4042
require "bundle/vscode_extension_checker"

lib/bundle/commands/services.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# typed: strict
2+
# frozen_string_literal: true
3+
4+
module Bundle
5+
module Commands
6+
module Services
7+
sig { params(args: String, global: T::Boolean, file: T.nilable(String)).void }
8+
def self.run(*args, global:, file:)
9+
raise UsageError, "invalid `brew bundle services` arguments" if args.length != 1
10+
11+
parsed_entries = Brewfile.read(global:, file:).entries
12+
13+
subcommand = args.first
14+
case subcommand
15+
when "run"
16+
Bundle::Services.run(parsed_entries)
17+
when "stop"
18+
Bundle::Services.stop(parsed_entries)
19+
else
20+
raise UsageError, "unknown services subcommand: #{subcommand}"
21+
end
22+
end
23+
end
24+
end
25+
end

lib/bundle/services.rb

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# typed: strict
2+
# frozen_string_literal: true
3+
4+
require "formula"
5+
require "services/cli"
6+
require "services/system"
7+
8+
module Bundle
9+
module Services
10+
sig {
11+
params(
12+
entries: T::Array[Bundle::Dsl::Entry],
13+
_block: T.proc.params(wrapper: Homebrew::Services::FormulaWrapper, 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+
wrapper = Homebrew::Services::FormulaWrapper.new(formula)
60+
61+
yield wrapper, service_file
62+
end
63+
end
64+
65+
sig { params(entries: T::Array[Bundle::Dsl::Entry]).void }
66+
def self.run(entries)
67+
map_entries(entries) do |wrapper, service_file|
68+
next if wrapper.pid? # already started
69+
70+
if Homebrew::Services::System.launchctl?
71+
Homebrew::Services::Cli.launchctl_load(wrapper, file: service_file, enable: false)
72+
elsif Homebrew::Services::System.systemctl?
73+
Homebrew::Services::Cli.install_service_file(wrapper, service_file)
74+
Homebrew::Services::Cli.systemd_load(wrapper, enable: false)
75+
end
76+
77+
ohai "Running service `#{wrapper.name}` (label: #{wrapper.service_name})"
78+
end
79+
end
80+
81+
sig { params(entries: T::Array[Bundle::Dsl::Entry]).void }
82+
def self.stop(entries)
83+
map_entries(entries) do |wrapper, _service_file|
84+
next unless wrapper.loaded?
85+
86+
# Try avoid services not started by `brew bundle services`
87+
next if Homebrew::Services::System.launchctl? && wrapper.dest.exist?
88+
89+
Homebrew::Services::Cli.stop([wrapper])
90+
end
91+
end
92+
end
93+
end

0 commit comments

Comments
 (0)