Merge pull request #15396 from apainintheneck/custom-service-name
Custom service name
This commit is contained in:
commit
946478aed4
@ -2,6 +2,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "language/python"
|
require "language/python"
|
||||||
|
require "utils/service"
|
||||||
|
|
||||||
# A formula's caveats.
|
# A formula's caveats.
|
||||||
#
|
#
|
||||||
@ -153,33 +154,32 @@ class Caveats
|
|||||||
end
|
end
|
||||||
|
|
||||||
def service_caveats
|
def service_caveats
|
||||||
return if !formula.plist && !formula.service? && !keg&.plist_installed?
|
return if !formula.plist && !formula.service? && !Utils::Service.installed?(formula) && !keg&.plist_installed?
|
||||||
return if formula.service? && formula.service.command.blank?
|
return if formula.service? && !formula.service.command? && !Utils::Service.installed?(formula)
|
||||||
|
|
||||||
s = []
|
s = []
|
||||||
|
|
||||||
command = if formula.service?
|
command = if formula.service.command?
|
||||||
formula.service.manual_command
|
formula.service.manual_command
|
||||||
else
|
else
|
||||||
formula.plist_manual
|
formula.plist_manual
|
||||||
end
|
end
|
||||||
|
|
||||||
return <<~EOS if !which("launchctl") && formula.plist
|
return <<~EOS if !Utils::Service.launchctl? && formula.plist
|
||||||
#{Formatter.warning("Warning:")} #{formula.name} provides a launchd plist which can only be used on macOS!
|
#{Formatter.warning("Warning:")} #{formula.name} provides a launchd plist which can only be used on macOS!
|
||||||
You can manually execute the service instead with:
|
You can manually execute the service instead with:
|
||||||
#{command}
|
#{command}
|
||||||
EOS
|
EOS
|
||||||
|
|
||||||
# Brew services only works with these two tools
|
# Brew services only works with these two tools
|
||||||
return <<~EOS if !which("systemctl") && !which("launchctl") && formula.service?
|
return <<~EOS if !Utils::Service.systemctl? && !Utils::Service.launchctl? && formula.service.command?
|
||||||
#{Formatter.warning("Warning:")} #{formula.name} provides a service which can only be used on macOS or systemd!
|
#{Formatter.warning("Warning:")} #{formula.name} provides a service which can only be used on macOS or systemd!
|
||||||
You can manually execute the service instead with:
|
You can manually execute the service instead with:
|
||||||
#{command}
|
#{command}
|
||||||
EOS
|
EOS
|
||||||
|
|
||||||
is_running_service = formula.service? && quiet_system("ps aux | grep #{formula.service.command&.first}")
|
startup = formula.service.requires_root? || formula.plist_startup
|
||||||
startup = formula.service&.requires_root? || formula.plist_startup
|
if Utils::Service.running?(formula)
|
||||||
if is_running_service || (formula.plist && quiet_system("/bin/launchctl list #{formula.plist_name} &>/dev/null"))
|
|
||||||
s << "To restart #{formula.full_name} after an upgrade:"
|
s << "To restart #{formula.full_name} after an upgrade:"
|
||||||
s << " #{startup ? "sudo " : ""}brew services restart #{formula.full_name}"
|
s << " #{startup ? "sudo " : ""}brew services restart #{formula.full_name}"
|
||||||
elsif startup
|
elsif startup
|
||||||
@ -190,7 +190,7 @@ class Caveats
|
|||||||
s << " brew services start #{formula.full_name}"
|
s << " brew services start #{formula.full_name}"
|
||||||
end
|
end
|
||||||
|
|
||||||
if formula.plist_manual || formula.service?
|
if formula.plist_manual || formula.service.command?
|
||||||
s << "Or, if you don't want/need a background service you can just run:"
|
s << "Or, if you don't want/need a background service you can just run:"
|
||||||
s << " #{command}"
|
s << " #{command}"
|
||||||
end
|
end
|
||||||
|
@ -1022,13 +1022,13 @@ class Formula
|
|||||||
# The generated launchd {.plist} service name.
|
# The generated launchd {.plist} service name.
|
||||||
sig { returns(String) }
|
sig { returns(String) }
|
||||||
def plist_name
|
def plist_name
|
||||||
"homebrew.mxcl.#{name}"
|
service.plist_name
|
||||||
end
|
end
|
||||||
|
|
||||||
# The generated service name.
|
# The generated service name.
|
||||||
sig { returns(String) }
|
sig { returns(String) }
|
||||||
def service_name
|
def service_name
|
||||||
"homebrew.#{name}"
|
service.service_name
|
||||||
end
|
end
|
||||||
|
|
||||||
# The generated launchd {.plist} file path.
|
# The generated launchd {.plist} file path.
|
||||||
@ -1058,8 +1058,6 @@ class Formula
|
|||||||
|
|
||||||
# The service specification of the software.
|
# The service specification of the software.
|
||||||
def service
|
def service
|
||||||
return unless service?
|
|
||||||
|
|
||||||
@service ||= Homebrew::Service.new(self, &self.class.service)
|
@service ||= Homebrew::Service.new(self, &self.class.service)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -2176,7 +2174,7 @@ class Formula
|
|||||||
"disabled" => disabled?,
|
"disabled" => disabled?,
|
||||||
"disable_date" => disable_date,
|
"disable_date" => disable_date,
|
||||||
"disable_reason" => disable_reason,
|
"disable_reason" => disable_reason,
|
||||||
"service" => service&.serialize,
|
"service" => (service.serialize if service?),
|
||||||
"tap_git_head" => tap_git_head,
|
"tap_git_head" => tap_git_head,
|
||||||
"ruby_source_path" => ruby_source_path,
|
"ruby_source_path" => ruby_source_path,
|
||||||
"ruby_source_checksum" => {},
|
"ruby_source_checksum" => {},
|
||||||
|
@ -288,7 +288,7 @@ module FormulaCellarChecks
|
|||||||
def check_service_command(formula)
|
def check_service_command(formula)
|
||||||
return unless formula.prefix.directory?
|
return unless formula.prefix.directory?
|
||||||
return unless formula.service?
|
return unless formula.service?
|
||||||
return if formula.service.command.blank?
|
return unless formula.service.command?
|
||||||
|
|
||||||
"Service command does not exist" unless File.exist?(formula.service.command.first)
|
"Service command does not exist" unless File.exist?(formula.service.command.first)
|
||||||
end
|
end
|
||||||
|
@ -1040,7 +1040,7 @@ on_request: installed_on_request?, options: options)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if formula.service? && formula.service.command.present?
|
if formula.service? && formula.service.command?
|
||||||
service_path = formula.systemd_service_path
|
service_path = formula.systemd_service_path
|
||||||
service_path.atomic_write(formula.service.to_systemd_unit)
|
service_path.atomic_write(formula.service.to_systemd_unit)
|
||||||
service_path.chmod 0644
|
service_path.chmod 0644
|
||||||
@ -1052,7 +1052,7 @@ on_request: installed_on_request?, options: options)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
service = if formula.service? && formula.service.command.present?
|
service = if formula.service? && formula.service.command?
|
||||||
formula.service.to_plist
|
formula.service.to_plist
|
||||||
elsif formula.plist
|
elsif formula.plist
|
||||||
formula.plist
|
formula.plist
|
||||||
|
@ -260,14 +260,22 @@ module Formulary
|
|||||||
|
|
||||||
if (service_hash = json_formula["service"])
|
if (service_hash = json_formula["service"])
|
||||||
service_hash = Homebrew::Service.deserialize(service_hash)
|
service_hash = Homebrew::Service.deserialize(service_hash)
|
||||||
run_params = service_hash.delete(:run)
|
|
||||||
service do
|
service do
|
||||||
T.bind(self, Homebrew::Service)
|
T.bind(self, Homebrew::Service)
|
||||||
if run_params.is_a?(Hash)
|
|
||||||
|
if (run_params = service_hash.delete(:run))
|
||||||
|
case run_params
|
||||||
|
when Hash
|
||||||
run(**run_params)
|
run(**run_params)
|
||||||
else
|
when Array, String
|
||||||
run run_params
|
run run_params
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if (name_params = service_hash.delete(:name))
|
||||||
|
name(**name_params)
|
||||||
|
end
|
||||||
|
|
||||||
service_hash.each do |key, arg|
|
service_hash.each do |key, arg|
|
||||||
public_send(key, arg)
|
public_send(key, arg)
|
||||||
end
|
end
|
||||||
|
@ -21,15 +21,35 @@ module RuboCop
|
|||||||
share: :opt_share,
|
share: :opt_share,
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
|
# At least one of these methods must be defined in a service block.
|
||||||
|
REQUIRED_METHOD_CALLS = [:run, :name].freeze
|
||||||
|
|
||||||
def audit_formula(_node, _class_node, _parent_class_node, body_node)
|
def audit_formula(_node, _class_node, _parent_class_node, body_node)
|
||||||
service_node = find_block(body_node, :service)
|
service_node = find_block(body_node, :service)
|
||||||
return if service_node.blank?
|
return if service_node.blank?
|
||||||
|
|
||||||
|
method_calls = service_node.each_descendant(:send).group_by(&:method_name)
|
||||||
|
method_calls.delete(:service)
|
||||||
|
|
||||||
|
# NOTE: Solving the first problem here might solve the second one too
|
||||||
|
# so we don't show both of them at the same time.
|
||||||
|
if (method_calls.keys & REQUIRED_METHOD_CALLS).empty?
|
||||||
|
offending_node(service_node)
|
||||||
|
problem "Service blocks require `run` or `name` to be defined."
|
||||||
|
elsif !method_calls.key?(:run)
|
||||||
|
other_method_calls = method_calls.keys - [:name]
|
||||||
|
if other_method_calls.any?
|
||||||
|
offending_node(service_node)
|
||||||
|
problem "`run` must be defined to use methods other than `name` like #{other_method_calls}."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# This check ensures that cellar paths like `bin` are not referenced
|
# This check ensures that cellar paths like `bin` are not referenced
|
||||||
# because their `opt_` variants are more portable and work with the
|
# because their `opt_` variants are more portable and work with the API.
|
||||||
# API.
|
|
||||||
CELLAR_PATH_AUDIT_CORRECTIONS.each do |path, opt_path|
|
CELLAR_PATH_AUDIT_CORRECTIONS.each do |path, opt_path|
|
||||||
find_every_method_call_by_name(service_node, path).each do |node|
|
next unless method_calls.key?(path)
|
||||||
|
|
||||||
|
method_calls.fetch(path).each do |node|
|
||||||
offending_node(node)
|
offending_node(node)
|
||||||
problem "Use `#{opt_path}` instead of `#{path}` in service blocks." do |corrector|
|
problem "Use `#{opt_path}` instead of `#{path}` in service blocks." do |corrector|
|
||||||
corrector.replace(node.source_range, opt_path)
|
corrector.replace(node.source_range, opt_path)
|
||||||
|
@ -28,7 +28,7 @@ module Homebrew
|
|||||||
@run_type = RUN_TYPE_IMMEDIATE
|
@run_type = RUN_TYPE_IMMEDIATE
|
||||||
@run_at_load = true
|
@run_at_load = true
|
||||||
@environment_variables = {}
|
@environment_variables = {}
|
||||||
@service_block = block
|
instance_eval(&block) if block
|
||||||
end
|
end
|
||||||
|
|
||||||
sig { returns(Formula) }
|
sig { returns(Formula) }
|
||||||
@ -36,6 +36,34 @@ module Homebrew
|
|||||||
@formula
|
@formula
|
||||||
end
|
end
|
||||||
|
|
||||||
|
sig { returns(String) }
|
||||||
|
def default_plist_name
|
||||||
|
"homebrew.mxcl.#{@formula.name}"
|
||||||
|
end
|
||||||
|
|
||||||
|
sig { returns(String) }
|
||||||
|
def plist_name
|
||||||
|
@plist_name ||= default_plist_name
|
||||||
|
end
|
||||||
|
|
||||||
|
sig { returns(String) }
|
||||||
|
def default_service_name
|
||||||
|
"homebrew.#{@formula.name}"
|
||||||
|
end
|
||||||
|
|
||||||
|
sig { returns(String) }
|
||||||
|
def service_name
|
||||||
|
@service_name ||= default_service_name
|
||||||
|
end
|
||||||
|
|
||||||
|
sig { params(macos: T.nilable(String), linux: T.nilable(String)).void }
|
||||||
|
def name(macos: nil, linux: nil)
|
||||||
|
raise TypeError, "Service#name expects at least one String" if [macos, linux].none?(String)
|
||||||
|
|
||||||
|
@plist_name = macos if macos
|
||||||
|
@service_name = linux if linux
|
||||||
|
end
|
||||||
|
|
||||||
sig {
|
sig {
|
||||||
params(
|
params(
|
||||||
command: T.nilable(T.any(T::Array[String], String, Pathname)),
|
command: T.nilable(T.any(T::Array[String], String, Pathname)),
|
||||||
@ -162,7 +190,6 @@ module Homebrew
|
|||||||
# @return [Boolean]
|
# @return [Boolean]
|
||||||
sig { returns(T::Boolean) }
|
sig { returns(T::Boolean) }
|
||||||
def requires_root?
|
def requires_root?
|
||||||
eval_service_block
|
|
||||||
@require_root.present? && @require_root == true
|
@require_root.present? && @require_root == true
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -198,7 +225,6 @@ module Homebrew
|
|||||||
# @return [Boolean]
|
# @return [Boolean]
|
||||||
sig { returns(T::Boolean) }
|
sig { returns(T::Boolean) }
|
||||||
def keep_alive?
|
def keep_alive?
|
||||||
eval_service_block
|
|
||||||
@keep_alive.present? && @keep_alive[:always] != false
|
@keep_alive.present? && @keep_alive[:always] != false
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -357,20 +383,22 @@ module Homebrew
|
|||||||
|
|
||||||
sig { returns(T.nilable(T::Array[String])) }
|
sig { returns(T.nilable(T::Array[String])) }
|
||||||
def command
|
def command
|
||||||
eval_service_block
|
|
||||||
@run&.map(&:to_s)
|
@run&.map(&:to_s)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
sig { returns(T::Boolean) }
|
||||||
|
def command?
|
||||||
|
@run.present?
|
||||||
|
end
|
||||||
|
|
||||||
# Returns the `String` command to run manually instead of the service.
|
# Returns the `String` command to run manually instead of the service.
|
||||||
# @return [String]
|
# @return [String]
|
||||||
sig { returns(String) }
|
sig { returns(String) }
|
||||||
def manual_command
|
def manual_command
|
||||||
eval_service_block
|
|
||||||
vars = @environment_variables.except(:PATH)
|
vars = @environment_variables.except(:PATH)
|
||||||
.map { |k, v| "#{k}=\"#{v}\"" }
|
.map { |k, v| "#{k}=\"#{v}\"" }
|
||||||
|
|
||||||
cmd = command
|
out = vars + command if command?
|
||||||
out = vars + cmd if cmd.present?
|
|
||||||
out.join(" ")
|
out.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -378,7 +406,6 @@ module Homebrew
|
|||||||
# @return [Boolean]
|
# @return [Boolean]
|
||||||
sig { returns(T::Boolean) }
|
sig { returns(T::Boolean) }
|
||||||
def timed?
|
def timed?
|
||||||
eval_service_block
|
|
||||||
@run_type == RUN_TYPE_CRON || @run_type == RUN_TYPE_INTERVAL
|
@run_type == RUN_TYPE_CRON || @run_type == RUN_TYPE_INTERVAL
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -388,7 +415,7 @@ module Homebrew
|
|||||||
def to_plist
|
def to_plist
|
||||||
# command needs to be first because it initializes all other values
|
# command needs to be first because it initializes all other values
|
||||||
base = {
|
base = {
|
||||||
Label: @formula.plist_name,
|
Label: plist_name,
|
||||||
ProgramArguments: command,
|
ProgramArguments: command,
|
||||||
RunAtLoad: @run_at_load == true,
|
RunAtLoad: @run_at_load == true,
|
||||||
}
|
}
|
||||||
@ -487,10 +514,9 @@ module Homebrew
|
|||||||
WantedBy=timers.target
|
WantedBy=timers.target
|
||||||
|
|
||||||
[Timer]
|
[Timer]
|
||||||
Unit=#{@formula.service_name}
|
Unit=#{service_name}
|
||||||
EOS
|
EOS
|
||||||
|
|
||||||
eval_service_block
|
|
||||||
options = []
|
options = []
|
||||||
options << "Persistent=true" if @run_type == RUN_TYPE_CRON
|
options << "Persistent=true" if @run_type == RUN_TYPE_CRON
|
||||||
options << "OnUnitActiveSec=#{@interval}" if @run_type == RUN_TYPE_INTERVAL
|
options << "OnUnitActiveSec=#{@interval}" if @run_type == RUN_TYPE_INTERVAL
|
||||||
@ -504,19 +530,17 @@ module Homebrew
|
|||||||
timer + options.join("\n")
|
timer + options.join("\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
# Only evaluate the service block once.
|
|
||||||
sig { void }
|
|
||||||
def eval_service_block
|
|
||||||
return if @eval_service_block
|
|
||||||
|
|
||||||
instance_eval(&@service_block)
|
|
||||||
@eval_service_block = true
|
|
||||||
end
|
|
||||||
|
|
||||||
# Prepare the service hash for inclusion in the formula API JSON.
|
# Prepare the service hash for inclusion in the formula API JSON.
|
||||||
sig { returns(Hash) }
|
sig { returns(Hash) }
|
||||||
def serialize
|
def serialize
|
||||||
eval_service_block
|
name_params = {
|
||||||
|
macos: (plist_name if plist_name != default_plist_name),
|
||||||
|
linux: (service_name if service_name != default_service_name),
|
||||||
|
}.compact
|
||||||
|
|
||||||
|
unless command?
|
||||||
|
return name_params.blank? ? {} : { name: name_params }
|
||||||
|
end
|
||||||
|
|
||||||
cron_string = if @cron.present?
|
cron_string = if @cron.present?
|
||||||
[:Minute, :Hour, :Day, :Month, :Weekday]
|
[:Minute, :Hour, :Day, :Month, :Weekday]
|
||||||
@ -527,6 +551,7 @@ module Homebrew
|
|||||||
sockets_string = "#{@sockets[:type]}://#{@sockets[:host]}:#{@sockets[:port]}" if @sockets.present?
|
sockets_string = "#{@sockets[:type]}://#{@sockets[:host]}:#{@sockets[:port]}" if @sockets.present?
|
||||||
|
|
||||||
{
|
{
|
||||||
|
name: name_params.presence,
|
||||||
run: @run_params,
|
run: @run_params,
|
||||||
run_type: @run_type,
|
run_type: @run_type,
|
||||||
interval: @interval,
|
interval: @interval,
|
||||||
@ -551,6 +576,12 @@ module Homebrew
|
|||||||
sig { params(api_hash: Hash).returns(Hash) }
|
sig { params(api_hash: Hash).returns(Hash) }
|
||||||
def self.deserialize(api_hash)
|
def self.deserialize(api_hash)
|
||||||
hash = {}
|
hash = {}
|
||||||
|
hash[:name] = api_hash["name"].transform_keys(&:to_sym) if api_hash.key?("name")
|
||||||
|
|
||||||
|
# The service hash might not have a "run" command if it only documents
|
||||||
|
# an existing service file with the "name" command.
|
||||||
|
return hash unless api_hash.key?("run")
|
||||||
|
|
||||||
hash[:run] =
|
hash[:run] =
|
||||||
case api_hash["run"]
|
case api_hash["run"]
|
||||||
when String
|
when String
|
||||||
|
@ -32,6 +32,10 @@ describe Caveats do
|
|||||||
|
|
||||||
describe "#caveats" do
|
describe "#caveats" do
|
||||||
context "when f.plist is not nil", :needs_macos do
|
context "when f.plist is not nil", :needs_macos do
|
||||||
|
before do
|
||||||
|
allow(Utils::Service).to receive(:launchctl?).and_return(true)
|
||||||
|
end
|
||||||
|
|
||||||
it "prints error when no launchd is present" do
|
it "prints error when no launchd is present" do
|
||||||
f = formula do
|
f = formula do
|
||||||
url "foo-1.0"
|
url "foo-1.0"
|
||||||
@ -39,7 +43,7 @@ describe Caveats do
|
|||||||
"plist_test.plist"
|
"plist_test.plist"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
allow_any_instance_of(Object).to receive(:which).with("launchctl").and_return(nil)
|
expect(Utils::Service).to receive(:launchctl?).once.and_return(false)
|
||||||
expect(described_class.new(f).caveats).to include("provides a launchd plist which can only be used on macOS!")
|
expect(described_class.new(f).caveats).to include("provides a launchd plist which can only be used on macOS!")
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -50,7 +54,7 @@ describe Caveats do
|
|||||||
"plist_test.plist"
|
"plist_test.plist"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
expect(described_class.new(f).caveats).to include("login")
|
expect(described_class.new(f).caveats).to include("restart at login")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "gives information about service" do
|
it "gives information about service" do
|
||||||
@ -82,12 +86,25 @@ describe Caveats do
|
|||||||
expect(caveats).to include("WARNING:")
|
expect(caveats).to include("WARNING:")
|
||||||
expect(caveats).to include("tmux")
|
expect(caveats).to include("tmux")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @todo This should get deprecated and the service block `plist_name` method should get used instead.
|
||||||
|
it "prints info when there are custom service files" do
|
||||||
|
f = formula do
|
||||||
|
url "foo-1.0"
|
||||||
|
def plist_name
|
||||||
|
"custom.mxcl.foo"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
expect(Utils::Service).to receive(:installed?).with(f).once.and_return(true)
|
||||||
|
expect(Utils::Service).to receive(:running?).with(f).once.and_return(false)
|
||||||
|
expect(described_class.new(f).caveats).to include("restart at login")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when f.service is not nil" do
|
context "when service block is defined" do
|
||||||
before do
|
before do
|
||||||
allow_any_instance_of(Object).to receive(:which).with("launchctl").and_return(true)
|
allow(Utils::Service).to receive(:launchctl?).and_return(true)
|
||||||
allow_any_instance_of(Object).to receive(:which).with("systemctl").and_return(true)
|
allow(Utils::Service).to receive(:systemctl?).and_return(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "prints warning when no service deamon is found" do
|
it "prints warning when no service deamon is found" do
|
||||||
@ -97,9 +114,8 @@ describe Caveats do
|
|||||||
run [bin/"cmd"]
|
run [bin/"cmd"]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
expect(Utils::Service).to receive(:launchctl?).twice.and_return(false)
|
||||||
allow_any_instance_of(Object).to receive(:which).with("launchctl").and_return(nil)
|
expect(Utils::Service).to receive(:systemctl?).once.and_return(false)
|
||||||
allow_any_instance_of(Object).to receive(:which).with("systemctl").and_return(nil)
|
|
||||||
expect(described_class.new(f).caveats).to include("service which can only be used on macOS or systemd!")
|
expect(described_class.new(f).caveats).to include("service which can only be used on macOS or systemd!")
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -111,9 +127,7 @@ describe Caveats do
|
|||||||
require_root true
|
require_root true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
cmd = "#{HOMEBREW_CELLAR}/formula_name/1.0/bin/cmd"
|
expect(Utils::Service).to receive(:running?).with(f).once.and_return(false)
|
||||||
allow(Homebrew).to receive(:_system).and_return(true)
|
|
||||||
allow(Homebrew).to receive(:_system).with("ps aux | grep #{cmd}").and_return(false)
|
|
||||||
expect(described_class.new(f).caveats).to include("startup")
|
expect(described_class.new(f).caveats).to include("startup")
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -124,10 +138,8 @@ describe Caveats do
|
|||||||
run [bin/"cmd"]
|
run [bin/"cmd"]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
cmd = "#{HOMEBREW_CELLAR}/formula_name/1.0/bin/cmd"
|
expect(Utils::Service).to receive(:running?).with(f).once.and_return(false)
|
||||||
allow(Homebrew).to receive(:_system).and_return(true)
|
expect(described_class.new(f).caveats).to include("restart at login")
|
||||||
expect(Homebrew).to receive(:_system).with("ps aux | grep #{cmd}").and_return(false)
|
|
||||||
expect(described_class.new(f).caveats).to include("login")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "gives information about require_root restarting services after upgrade" do
|
it "gives information about require_root restarting services after upgrade" do
|
||||||
@ -138,10 +150,8 @@ describe Caveats do
|
|||||||
require_root true
|
require_root true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
cmd = "#{HOMEBREW_CELLAR}/formula_name/1.0/bin/cmd"
|
|
||||||
f_obj = described_class.new(f)
|
f_obj = described_class.new(f)
|
||||||
allow(Homebrew).to receive(:_system).and_return(true)
|
expect(Utils::Service).to receive(:running?).with(f).once.and_return(true)
|
||||||
expect(Homebrew).to receive(:_system).with("ps aux | grep #{cmd}").and_return(true)
|
|
||||||
expect(f_obj.caveats).to include(" sudo brew services restart #{f.full_name}")
|
expect(f_obj.caveats).to include(" sudo brew services restart #{f.full_name}")
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -152,10 +162,8 @@ describe Caveats do
|
|||||||
run [bin/"cmd"]
|
run [bin/"cmd"]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
cmd = "#{HOMEBREW_CELLAR}/formula_name/1.0/bin/cmd"
|
|
||||||
f_obj = described_class.new(f)
|
f_obj = described_class.new(f)
|
||||||
allow(Homebrew).to receive(:_system).and_return(true)
|
expect(Utils::Service).to receive(:running?).with(f).once.and_return(true)
|
||||||
expect(Homebrew).to receive(:_system).with("ps aux | grep #{cmd}").and_return(true)
|
|
||||||
expect(f_obj.caveats).to include(" brew services restart #{f.full_name}")
|
expect(f_obj.caveats).to include(" brew services restart #{f.full_name}")
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -167,10 +175,8 @@ describe Caveats do
|
|||||||
require_root true
|
require_root true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
cmd = "#{HOMEBREW_CELLAR}/formula_name/1.0/bin/cmd"
|
|
||||||
f_obj = described_class.new(f)
|
f_obj = described_class.new(f)
|
||||||
allow(Homebrew).to receive(:_system).and_return(true)
|
expect(Utils::Service).to receive(:running?).with(f).once.and_return(false)
|
||||||
allow(Homebrew).to receive(:_system).with("ps aux | grep #{cmd}").and_return(false)
|
|
||||||
expect(f_obj.caveats).to include(" sudo brew services start #{f.full_name}")
|
expect(f_obj.caveats).to include(" sudo brew services start #{f.full_name}")
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -181,10 +187,8 @@ describe Caveats do
|
|||||||
run [bin/"cmd"]
|
run [bin/"cmd"]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
cmd = "#{HOMEBREW_CELLAR}/formula_name/1.0/bin/cmd"
|
|
||||||
f_obj = described_class.new(f)
|
f_obj = described_class.new(f)
|
||||||
allow(Homebrew).to receive(:_system).and_return(true)
|
expect(Utils::Service).to receive(:running?).with(f).once.and_return(false)
|
||||||
allow(Homebrew).to receive(:_system).with("ps aux | grep #{cmd}").and_return(false)
|
|
||||||
expect(f_obj.caveats).to include(" brew services start #{f.full_name}")
|
expect(f_obj.caveats).to include(" brew services start #{f.full_name}")
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -202,6 +206,18 @@ describe Caveats do
|
|||||||
expect(caveats).to include("if you don't want/need a background service")
|
expect(caveats).to include("if you don't want/need a background service")
|
||||||
expect(caveats).to include("VAR=\"foo\" #{cmd} start")
|
expect(caveats).to include("VAR=\"foo\" #{cmd} start")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "prints info when there are custom service files" do
|
||||||
|
f = formula do
|
||||||
|
url "foo-1.0"
|
||||||
|
service do
|
||||||
|
name macos: "custom.mxcl.foo", linux: "custom.foo"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
expect(Utils::Service).to receive(:installed?).with(f).once.and_return(true)
|
||||||
|
expect(Utils::Service).to receive(:running?).with(f).once.and_return(false)
|
||||||
|
expect(described_class.new(f).caveats).to include("restart at login")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when f.keg_only is not nil" do
|
context "when f.keg_only is not nil" do
|
||||||
|
@ -218,21 +218,21 @@ describe FormulaInstaller do
|
|||||||
|
|
||||||
it "works if service is set" do
|
it "works if service is set" do
|
||||||
formula = Testball.new
|
formula = Testball.new
|
||||||
|
service = Homebrew::Service.new(formula)
|
||||||
launchd_service_path = formula.launchd_service_path
|
launchd_service_path = formula.launchd_service_path
|
||||||
service_path = formula.systemd_service_path
|
service_path = formula.systemd_service_path
|
||||||
service = Homebrew::Service.new(formula)
|
|
||||||
formula.opt_prefix.mkpath
|
formula.opt_prefix.mkpath
|
||||||
|
|
||||||
expect(formula).to receive(:plist).and_return(nil)
|
expect(formula).to receive(:plist).and_return(nil)
|
||||||
expect(formula).to receive(:service?).exactly(3).and_return(true)
|
expect(formula).to receive(:service?).exactly(3).and_return(true)
|
||||||
expect(formula).to receive(:service).exactly(5).and_return(service)
|
expect(formula).to receive(:service).exactly(7).and_return(service)
|
||||||
expect(formula).to receive(:launchd_service_path).and_call_original
|
expect(formula).to receive(:launchd_service_path).and_call_original
|
||||||
expect(formula).to receive(:systemd_service_path).and_call_original
|
expect(formula).to receive(:systemd_service_path).and_call_original
|
||||||
|
|
||||||
expect(service).to receive(:timed?).and_return(false)
|
expect(service).to receive(:timed?).and_return(false)
|
||||||
|
expect(service).to receive(:command?).exactly(2).and_return(true)
|
||||||
expect(service).to receive(:to_plist).and_return("plist")
|
expect(service).to receive(:to_plist).and_return("plist")
|
||||||
expect(service).to receive(:to_systemd_unit).and_return("unit")
|
expect(service).to receive(:to_systemd_unit).and_return("unit")
|
||||||
expect(service).to receive(:command).exactly(2).and_return("/bin/sh")
|
|
||||||
|
|
||||||
installer = described_class.new(formula)
|
installer = described_class.new(formula)
|
||||||
expect do
|
expect do
|
||||||
@ -245,24 +245,24 @@ describe FormulaInstaller do
|
|||||||
|
|
||||||
it "works if timed service is set" do
|
it "works if timed service is set" do
|
||||||
formula = Testball.new
|
formula = Testball.new
|
||||||
|
service = Homebrew::Service.new(formula)
|
||||||
launchd_service_path = formula.launchd_service_path
|
launchd_service_path = formula.launchd_service_path
|
||||||
service_path = formula.systemd_service_path
|
service_path = formula.systemd_service_path
|
||||||
timer_path = formula.systemd_timer_path
|
timer_path = formula.systemd_timer_path
|
||||||
service = Homebrew::Service.new(formula)
|
|
||||||
formula.opt_prefix.mkpath
|
formula.opt_prefix.mkpath
|
||||||
|
|
||||||
expect(formula).to receive(:plist).and_return(nil)
|
expect(formula).to receive(:plist).and_return(nil)
|
||||||
expect(formula).to receive(:service?).exactly(3).and_return(true)
|
expect(formula).to receive(:service?).exactly(3).and_return(true)
|
||||||
expect(formula).to receive(:service).exactly(6).and_return(service)
|
expect(formula).to receive(:service).exactly(9).and_return(service)
|
||||||
expect(formula).to receive(:launchd_service_path).and_call_original
|
expect(formula).to receive(:launchd_service_path).and_call_original
|
||||||
expect(formula).to receive(:systemd_service_path).and_call_original
|
expect(formula).to receive(:systemd_service_path).and_call_original
|
||||||
expect(formula).to receive(:systemd_timer_path).and_call_original
|
expect(formula).to receive(:systemd_timer_path).and_call_original
|
||||||
|
|
||||||
expect(service).to receive(:to_plist).and_return("plist")
|
|
||||||
expect(service).to receive(:timed?).and_return(true)
|
expect(service).to receive(:timed?).and_return(true)
|
||||||
|
expect(service).to receive(:command?).exactly(2).and_return(true)
|
||||||
|
expect(service).to receive(:to_plist).and_return("plist")
|
||||||
expect(service).to receive(:to_systemd_unit).and_return("unit")
|
expect(service).to receive(:to_systemd_unit).and_return("unit")
|
||||||
expect(service).to receive(:to_systemd_timer).and_return("timer")
|
expect(service).to receive(:to_systemd_timer).and_return("timer")
|
||||||
expect(service).to receive(:command).exactly(2).and_return("/bin/sh")
|
|
||||||
|
|
||||||
installer = described_class.new(formula)
|
installer = described_class.new(formula)
|
||||||
expect do
|
expect do
|
||||||
|
@ -701,7 +701,7 @@ describe Formula do
|
|||||||
url "https://brew.sh/test-1.0.tbz"
|
url "https://brew.sh/test-1.0.tbz"
|
||||||
end
|
end
|
||||||
|
|
||||||
expect(f.service).to be_nil
|
expect(f.service.serialize).to eq({})
|
||||||
end
|
end
|
||||||
|
|
||||||
specify "service complicated" do
|
specify "service complicated" do
|
||||||
@ -717,7 +717,8 @@ describe Formula do
|
|||||||
keep_alive true
|
keep_alive true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
expect(f.service).not_to be_nil
|
expect(f.service.serialize.keys)
|
||||||
|
.to contain_exactly(:run, :run_type, :error_log_path, :log_path, :working_dir, :keep_alive)
|
||||||
end
|
end
|
||||||
|
|
||||||
specify "service uses simple run" do
|
specify "service uses simple run" do
|
||||||
@ -728,7 +729,20 @@ describe Formula do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
expect(f.service).not_to be_nil
|
expect(f.service.serialize.keys).to contain_exactly(:run, :run_type)
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "service with only custom names" do
|
||||||
|
f = formula do
|
||||||
|
url "https://brew.sh/test-1.0.tbz"
|
||||||
|
service do
|
||||||
|
name macos: "custom.macos.beanstalkd", linux: "custom.linux.beanstalkd"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(f.plist_name).to eq("custom.macos.beanstalkd")
|
||||||
|
expect(f.service_name).to eq("custom.linux.beanstalkd")
|
||||||
|
expect(f.service.serialize.keys).to contain_exactly(:name)
|
||||||
end
|
end
|
||||||
|
|
||||||
specify "service helpers return data" do
|
specify "service helpers return data" do
|
||||||
|
@ -272,6 +272,7 @@ describe Formulary do
|
|||||||
"link_overwrite" => ["bin/abc"],
|
"link_overwrite" => ["bin/abc"],
|
||||||
"caveats" => "example caveat string\n/$HOME\n$HOMEBREW_PREFIX",
|
"caveats" => "example caveat string\n/$HOME\n$HOMEBREW_PREFIX",
|
||||||
"service" => {
|
"service" => {
|
||||||
|
"name" => { macos: "custom.launchd.name", linux: "custom.systemd.name" },
|
||||||
"run" => ["$HOMEBREW_PREFIX/opt/formula_name/bin/beanstalkd", "test"],
|
"run" => ["$HOMEBREW_PREFIX/opt/formula_name/bin/beanstalkd", "test"],
|
||||||
"run_type" => "immediate",
|
"run_type" => "immediate",
|
||||||
"working_dir" => "/$HOME",
|
"working_dir" => "/$HOME",
|
||||||
@ -362,6 +363,8 @@ describe Formulary do
|
|||||||
expect(formula.service.command).to eq(["#{HOMEBREW_PREFIX}/opt/formula_name/bin/beanstalkd", "test"])
|
expect(formula.service.command).to eq(["#{HOMEBREW_PREFIX}/opt/formula_name/bin/beanstalkd", "test"])
|
||||||
expect(formula.service.run_type).to eq(:immediate)
|
expect(formula.service.run_type).to eq(:immediate)
|
||||||
expect(formula.service.working_dir).to eq(Dir.home)
|
expect(formula.service.working_dir).to eq(Dir.home)
|
||||||
|
expect(formula.plist_name).to eq("custom.launchd.name")
|
||||||
|
expect(formula.service_name).to eq("custom.systemd.name")
|
||||||
|
|
||||||
expect do
|
expect do
|
||||||
formula.install
|
formula.install
|
||||||
|
@ -5,6 +5,46 @@ require "rubocops/service"
|
|||||||
describe RuboCop::Cop::FormulaAudit::Service do
|
describe RuboCop::Cop::FormulaAudit::Service do
|
||||||
subject(:cop) { described_class.new }
|
subject(:cop) { described_class.new }
|
||||||
|
|
||||||
|
it "reports offenses when a service block is missing a required command" do
|
||||||
|
expect_offense(<<~RUBY)
|
||||||
|
class Foo < Formula
|
||||||
|
url "https://brew.sh/foo-1.0.tgz"
|
||||||
|
|
||||||
|
service do
|
||||||
|
^^^^^^^^^^ FormulaAudit/Service: Service blocks require `run` or `name` to be defined.
|
||||||
|
run_type :cron
|
||||||
|
working_dir "/tmp/example"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
|
||||||
|
it "reports no offenses when a service block only includes custom names" do
|
||||||
|
expect_no_offenses(<<~RUBY)
|
||||||
|
class Foo < Formula
|
||||||
|
url "https://brew.sh/foo-1.0.tgz"
|
||||||
|
|
||||||
|
service do
|
||||||
|
name macos: "custom.mcxl.foo", linux: "custom.foo"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
|
||||||
|
it "reports offenses when a service block includes more than custom names and no run command" do
|
||||||
|
expect_offense(<<~RUBY)
|
||||||
|
class Foo < Formula
|
||||||
|
url "https://brew.sh/foo-1.0.tgz"
|
||||||
|
|
||||||
|
service do
|
||||||
|
^^^^^^^^^^ FormulaAudit/Service: `run` must be defined to use methods other than `name` like [:working_dir].
|
||||||
|
name macos: "custom.mcxl.foo", linux: "custom.foo"
|
||||||
|
working_dir "/tmp/example"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
|
||||||
it "reports offenses when a formula's service block uses cellar paths" do
|
it "reports offenses when a formula's service block uses cellar paths" do
|
||||||
expect_offense(<<~RUBY)
|
expect_offense(<<~RUBY)
|
||||||
class Foo < Formula
|
class Foo < Formula
|
||||||
@ -31,7 +71,7 @@ describe RuboCop::Cop::FormulaAudit::Service do
|
|||||||
RUBY
|
RUBY
|
||||||
end
|
end
|
||||||
|
|
||||||
it "reports no offenses when a formula's service block only uses opt paths" do
|
it "reports no offenses when a service block only uses opt paths" do
|
||||||
expect_no_offenses(<<~RUBY)
|
expect_no_offenses(<<~RUBY)
|
||||||
class Bin < Formula
|
class Bin < Formula
|
||||||
url "https://brew.sh/foo-1.0.tgz"
|
url "https://brew.sh/foo-1.0.tgz"
|
||||||
|
@ -949,6 +949,10 @@ describe Homebrew::Service do
|
|||||||
describe ".deserialize" do
|
describe ".deserialize" do
|
||||||
let(:serialized_hash) do
|
let(:serialized_hash) do
|
||||||
{
|
{
|
||||||
|
"name" => {
|
||||||
|
"linux" => "custom.systemd.name",
|
||||||
|
"macos" => "custom.launchd.name",
|
||||||
|
},
|
||||||
"environment_variables" => {
|
"environment_variables" => {
|
||||||
"PATH" => "$HOMEBREW_PREFIX/bin:$HOMEBREW_PREFIX/sbin:/usr/bin:/bin:/usr/sbin:/sbin",
|
"PATH" => "$HOMEBREW_PREFIX/bin:$HOMEBREW_PREFIX/sbin:/usr/bin:/bin:/usr/sbin:/sbin",
|
||||||
},
|
},
|
||||||
@ -961,6 +965,10 @@ describe Homebrew::Service do
|
|||||||
|
|
||||||
let(:deserialized_hash) do
|
let(:deserialized_hash) do
|
||||||
{
|
{
|
||||||
|
name: {
|
||||||
|
linux: "custom.systemd.name",
|
||||||
|
macos: "custom.launchd.name",
|
||||||
|
},
|
||||||
environment_variables: {
|
environment_variables: {
|
||||||
PATH: "#{HOMEBREW_PREFIX}/bin:#{HOMEBREW_PREFIX}/sbin:/usr/bin:/bin:/usr/sbin:/sbin",
|
PATH: "#{HOMEBREW_PREFIX}/bin:#{HOMEBREW_PREFIX}/sbin:/usr/bin:/bin:/usr/sbin:/sbin",
|
||||||
},
|
},
|
||||||
|
50
Library/Homebrew/utils/service.rb
Normal file
50
Library/Homebrew/utils/service.rb
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
# typed: true
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Utils
|
||||||
|
# Helpers for `brew services` related code.
|
||||||
|
module Service
|
||||||
|
# Check if a service is running for a specified formula.
|
||||||
|
sig { params(formula: Formula).returns(T::Boolean) }
|
||||||
|
def self.running?(formula)
|
||||||
|
if launchctl?
|
||||||
|
quiet_system(launchctl, "list", formula.plist_name)
|
||||||
|
elsif systemctl?
|
||||||
|
quiet_system(systemctl, "is-active", "--quiet", formula.service_name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check if a service file is installed in the expected location.
|
||||||
|
sig { params(formula: Formula).returns(T::Boolean) }
|
||||||
|
def self.installed?(formula)
|
||||||
|
(launchctl? && formula.launchd_service_path.exist?) ||
|
||||||
|
(systemctl? && formula.systemd_service_path.exist?)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Path to launchctl binary.
|
||||||
|
sig { returns(T.nilable(Pathname)) }
|
||||||
|
def self.launchctl
|
||||||
|
return @launchctl if defined? @launchctl
|
||||||
|
|
||||||
|
@launchctl = which("launchctl")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Path to systemctl binary.
|
||||||
|
sig { returns(T.nilable(Pathname)) }
|
||||||
|
def self.systemctl
|
||||||
|
return @systemctl if defined? @systemctl
|
||||||
|
|
||||||
|
@systemctl = which("systemctl")
|
||||||
|
end
|
||||||
|
|
||||||
|
sig { returns(T::Boolean) }
|
||||||
|
def self.launchctl?
|
||||||
|
!launchctl.nil?
|
||||||
|
end
|
||||||
|
|
||||||
|
sig { returns(T::Boolean) }
|
||||||
|
def self.systemctl?
|
||||||
|
!systemctl.nil?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
7
Library/Homebrew/utils/service.rbi
Normal file
7
Library/Homebrew/utils/service.rbi
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# typed: strict
|
||||||
|
|
||||||
|
module Utils
|
||||||
|
module Service
|
||||||
|
include Kernel
|
||||||
|
end
|
||||||
|
end
|
@ -871,13 +871,17 @@ Another example would be configuration files that should not be overwritten on p
|
|||||||
|
|
||||||
There are two ways to add `launchd` plists and `systemd` services to a formula, so that [`brew services`](https://github.com/Homebrew/homebrew-services) can pick them up:
|
There are two ways to add `launchd` plists and `systemd` services to a formula, so that [`brew services`](https://github.com/Homebrew/homebrew-services) can pick them up:
|
||||||
|
|
||||||
1. If the package already provides a service file the formula can install it into the prefix:
|
1. If the package already provides a service file the formula can reference it by name:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
prefix.install_symlink "file.plist" => "#{plist_name}.plist"
|
service do
|
||||||
prefix.install_symlink "file.service" => "#{service_name}.service"
|
name macos: "custom.launchd.name",
|
||||||
|
linux: "custom.systemd.name"
|
||||||
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To find the file we append `.plist` to the `launchd` service name and `.service` to the `systemd` service name internally.
|
||||||
|
|
||||||
2. If the formula does not provide a service file you can generate one using the following stanza:
|
2. If the formula does not provide a service file you can generate one using the following stanza:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
@ -900,7 +904,7 @@ There are two ways to add `launchd` plists and `systemd` services to a formula,
|
|||||||
|
|
||||||
#### Service block methods
|
#### Service block methods
|
||||||
|
|
||||||
This table lists the options you can set within a `service` block. Only the `run` field is required which indicates what to run.
|
This table lists the options you can set within a `service` block. The `run` or `name` field must be defined inside the service block. The `run` field indicates what command to run and is required before using fields other than `name`.
|
||||||
|
|
||||||
| method | default | macOS | Linux | description |
|
| method | default | macOS | Linux | description |
|
||||||
| ----------------------- | ------------ | :---: | :---: | ----------- |
|
| ----------------------- | ------------ | :---: | :---: | ----------- |
|
||||||
@ -921,6 +925,7 @@ This table lists the options you can set within a `service` block. Only the `run
|
|||||||
| `process_type` | - | yes | no-op | type of process to manage: `:background`, `:standard`, `:interactive` or `:adaptive`
|
| `process_type` | - | yes | no-op | type of process to manage: `:background`, `:standard`, `:interactive` or `:adaptive`
|
||||||
| `macos_legacy_timers` | - | yes | no-op | timers created by `launchd` jobs are coalesced unless this is set
|
| `macos_legacy_timers` | - | yes | no-op | timers created by `launchd` jobs are coalesced unless this is set
|
||||||
| `sockets` | - | yes | no-op | socket that is created as an accesspoint to the service
|
| `sockets` | - | yes | no-op | socket that is created as an accesspoint to the service
|
||||||
|
| `name` | - | yes | yes | a hash with the `launchd` service name on macOS and/or the `systemd` service name on Linux
|
||||||
|
|
||||||
For services that are kept alive after starting you can use the default `run_type`:
|
For services that are kept alive after starting you can use the default `run_type`:
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user