Merge pull request #20324 from Homebrew/dug/low-shape-service
Reduce shape variations in Homebrew::Service
This commit is contained in:
commit
3b61b44c8a
@ -306,7 +306,7 @@ module FormulaCellarChecks
|
|||||||
return unless formula.service?
|
return unless formula.service?
|
||||||
return unless formula.service.command?
|
return unless formula.service.command?
|
||||||
|
|
||||||
"Service command does not exist" unless File.exist?(T.must(formula.service.command).first)
|
"Service command does not exist" unless File.exist?(formula.service.command.first)
|
||||||
end
|
end
|
||||||
|
|
||||||
sig { params(formula: Formula).returns(T.nilable(String)) }
|
sig { params(formula: Formula).returns(T.nilable(String)) }
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
# typed: strict
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "ipaddr"
|
require "ipaddr"
|
||||||
@ -23,13 +23,38 @@ module Homebrew
|
|||||||
PROCESS_TYPE_ADAPTIVE = :adaptive
|
PROCESS_TYPE_ADAPTIVE = :adaptive
|
||||||
|
|
||||||
KEEP_ALIVE_KEYS = [:always, :successful_exit, :crashed, :path].freeze
|
KEEP_ALIVE_KEYS = [:always, :successful_exit, :crashed, :path].freeze
|
||||||
|
SOCKET_STRING_REGEX = %r{^([a-z]+)://(.+):([0-9]+)$}i
|
||||||
|
|
||||||
# sig { params(formula: Formula).void }
|
RunParam = T.type_alias { T.nilable(T.any(T::Array[T.any(String, Pathname)], String, Pathname)) }
|
||||||
|
Sockets = T.type_alias { T::Hash[Symbol, { host: String, port: String, type: String }] }
|
||||||
|
|
||||||
|
sig { returns(String) }
|
||||||
|
attr_reader :plist_name, :service_name
|
||||||
|
|
||||||
|
sig { params(formula: Formula, block: T.nilable(T.proc.void)).void }
|
||||||
def initialize(formula, &block)
|
def initialize(formula, &block)
|
||||||
|
@cron = T.let({}, T::Hash[Symbol, T.any(Integer, String)])
|
||||||
|
@environment_variables = T.let({}, T::Hash[Symbol, String])
|
||||||
|
@error_log_path = T.let(nil, T.nilable(String))
|
||||||
@formula = formula
|
@formula = formula
|
||||||
@run_type = RUN_TYPE_IMMEDIATE
|
@input_path = T.let(nil, T.nilable(String))
|
||||||
@run_at_load = true
|
@interval = T.let(nil, T.nilable(Integer))
|
||||||
@environment_variables = {}
|
@keep_alive = T.let({}, T::Hash[Symbol, T.untyped])
|
||||||
|
@launch_only_once = T.let(false, T::Boolean)
|
||||||
|
@log_path = T.let(nil, T.nilable(String))
|
||||||
|
@macos_legacy_timers = T.let(false, T::Boolean)
|
||||||
|
@plist_name = T.let(default_plist_name, String)
|
||||||
|
@process_type = T.let(nil, T.nilable(Symbol))
|
||||||
|
@require_root = T.let(false, T::Boolean)
|
||||||
|
@restart_delay = T.let(nil, T.nilable(Integer))
|
||||||
|
@root_dir = T.let(nil, T.nilable(String))
|
||||||
|
@run = T.let([], T::Array[String])
|
||||||
|
@run_at_load = T.let(true, T::Boolean)
|
||||||
|
@run_params = T.let(nil, T.any(RunParam, T::Hash[Symbol, RunParam]))
|
||||||
|
@run_type = T.let(RUN_TYPE_IMMEDIATE, Symbol)
|
||||||
|
@service_name = T.let(default_service_name, String)
|
||||||
|
@sockets = T.let({}, Sockets)
|
||||||
|
@working_dir = T.let(nil, T.nilable(String))
|
||||||
instance_eval(&block) if block
|
instance_eval(&block) if block
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -43,21 +68,11 @@ module Homebrew
|
|||||||
"homebrew.mxcl.#{@formula.name}"
|
"homebrew.mxcl.#{@formula.name}"
|
||||||
end
|
end
|
||||||
|
|
||||||
sig { returns(String) }
|
|
||||||
def plist_name
|
|
||||||
@plist_name ||= default_plist_name
|
|
||||||
end
|
|
||||||
|
|
||||||
sig { returns(String) }
|
sig { returns(String) }
|
||||||
def default_service_name
|
def default_service_name
|
||||||
"homebrew.#{@formula.name}"
|
"homebrew.#{@formula.name}"
|
||||||
end
|
end
|
||||||
|
|
||||||
sig { returns(String) }
|
|
||||||
def service_name
|
|
||||||
@service_name ||= default_service_name
|
|
||||||
end
|
|
||||||
|
|
||||||
sig { params(macos: T.nilable(String), linux: T.nilable(String)).void }
|
sig { params(macos: T.nilable(String), linux: T.nilable(String)).void }
|
||||||
def name(macos: nil, linux: nil)
|
def name(macos: nil, linux: nil)
|
||||||
raise TypeError, "Service#name expects at least one String" if [macos, linux].none?(String)
|
raise TypeError, "Service#name expects at least one String" if [macos, linux].none?(String)
|
||||||
@ -68,9 +83,9 @@ module Homebrew
|
|||||||
|
|
||||||
sig {
|
sig {
|
||||||
params(
|
params(
|
||||||
command: T.nilable(T.any(T::Array[T.any(String, Pathname)], String, Pathname)),
|
command: T.nilable(RunParam),
|
||||||
macos: T.nilable(T.any(T::Array[T.any(String, Pathname)], String, Pathname)),
|
macos: T.nilable(RunParam),
|
||||||
linux: T.nilable(T.any(T::Array[T.any(String, Pathname)], String, Pathname)),
|
linux: T.nilable(RunParam),
|
||||||
).returns(T.nilable(T::Array[T.any(String, Pathname)]))
|
).returns(T.nilable(T::Array[T.any(String, Pathname)]))
|
||||||
}
|
}
|
||||||
def run(command = nil, macos: nil, linux: nil)
|
def run(command = nil, macos: nil, linux: nil)
|
||||||
@ -86,9 +101,9 @@ module Homebrew
|
|||||||
when nil
|
when nil
|
||||||
@run
|
@run
|
||||||
when String, Pathname
|
when String, Pathname
|
||||||
@run = [command]
|
@run = [command.to_s]
|
||||||
when Array
|
when Array
|
||||||
@run = command
|
@run = command.map(&:to_s)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -161,18 +176,17 @@ module Homebrew
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
sig { params(value: T.nilable(T::Boolean)).returns(T.nilable(T::Boolean)) }
|
sig { params(value: T.nilable(T::Boolean)).returns(T::Boolean) }
|
||||||
def require_root(value = nil)
|
def require_root(value = nil)
|
||||||
case value
|
case value
|
||||||
when nil
|
when nil
|
||||||
@require_root
|
@require_root
|
||||||
when true, false
|
when TrueClass, FalseClass
|
||||||
@require_root = value
|
@require_root = value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a `Boolean` describing if a service requires root access.
|
# Returns a `Boolean` describing if a service requires root access.
|
||||||
# @return [Boolean]
|
|
||||||
sig { returns(T::Boolean) }
|
sig { returns(T::Boolean) }
|
||||||
def requires_root?
|
def requires_root?
|
||||||
@require_root.present? && @require_root == true
|
@require_root.present? && @require_root == true
|
||||||
@ -183,16 +197,14 @@ module Homebrew
|
|||||||
case value
|
case value
|
||||||
when nil
|
when nil
|
||||||
@run_at_load
|
@run_at_load
|
||||||
when true, false
|
when TrueClass, FalseClass
|
||||||
@run_at_load = value
|
@run_at_load = value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
SOCKET_STRING_REGEX = %r{^([a-z]+)://(.+):([0-9]+)$}i
|
|
||||||
|
|
||||||
sig {
|
sig {
|
||||||
params(value: T.nilable(T.any(String, T::Hash[Symbol, String])))
|
params(value: T.nilable(T.any(String, T::Hash[Symbol, String])))
|
||||||
.returns(T.nilable(T::Hash[Symbol, T::Hash[Symbol, String]]))
|
.returns(T::Hash[Symbol, T::Hash[Symbol, String]])
|
||||||
}
|
}
|
||||||
def sockets(value = nil)
|
def sockets(value = nil)
|
||||||
return @sockets if value.nil?
|
return @sockets if value.nil?
|
||||||
@ -204,9 +216,11 @@ module Homebrew
|
|||||||
value
|
value
|
||||||
end.transform_values do |socket_string|
|
end.transform_values do |socket_string|
|
||||||
match = socket_string.match(SOCKET_STRING_REGEX)
|
match = socket_string.match(SOCKET_STRING_REGEX)
|
||||||
raise TypeError, "Service#sockets a formatted socket definition as <type>://<host>:<port>" if match.blank?
|
raise TypeError, "Service#sockets a formatted socket definition as <type>://<host>:<port>" unless match
|
||||||
|
|
||||||
type, host, port = match.captures
|
type = T.must(match[1])
|
||||||
|
host = T.must(match[2])
|
||||||
|
port = T.must(match[3])
|
||||||
|
|
||||||
begin
|
begin
|
||||||
IPAddr.new(host)
|
IPAddr.new(host)
|
||||||
@ -219,18 +233,17 @@ module Homebrew
|
|||||||
end
|
end
|
||||||
|
|
||||||
# Returns a `Boolean` describing if a service is set to be kept alive.
|
# Returns a `Boolean` describing if a service is set to be kept alive.
|
||||||
# @return [Boolean]
|
|
||||||
sig { returns(T::Boolean) }
|
sig { returns(T::Boolean) }
|
||||||
def keep_alive?
|
def keep_alive?
|
||||||
@keep_alive.present? && @keep_alive[:always] != false
|
!@keep_alive.empty? && @keep_alive[:always] != false
|
||||||
end
|
end
|
||||||
|
|
||||||
sig { params(value: T.nilable(T::Boolean)).returns(T.nilable(T::Boolean)) }
|
sig { params(value: T.nilable(T::Boolean)).returns(T::Boolean) }
|
||||||
def launch_only_once(value = nil)
|
def launch_only_once(value = nil)
|
||||||
case value
|
case value
|
||||||
when nil
|
when nil
|
||||||
@launch_only_once
|
@launch_only_once
|
||||||
when true, false
|
when TrueClass, FalseClass
|
||||||
@launch_only_once = value
|
@launch_only_once = value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -281,13 +294,13 @@ module Homebrew
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
sig { params(value: T.nilable(String)).returns(T.nilable(Hash)) }
|
sig { params(value: T.nilable(String)).returns(T::Hash[Symbol, T.any(Integer, String)]) }
|
||||||
def cron(value = nil)
|
def cron(value = nil)
|
||||||
case value
|
case value
|
||||||
when nil
|
when nil
|
||||||
@cron
|
@cron
|
||||||
when String
|
when String
|
||||||
@cron = parse_cron(T.must(value))
|
@cron = parse_cron(value)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -345,12 +358,12 @@ module Homebrew
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
sig { params(value: T.nilable(T::Boolean)).returns(T.nilable(T::Boolean)) }
|
sig { params(value: T.nilable(T::Boolean)).returns(T::Boolean) }
|
||||||
def macos_legacy_timers(value = nil)
|
def macos_legacy_timers(value = nil)
|
||||||
case value
|
case value
|
||||||
when nil
|
when nil
|
||||||
@macos_legacy_timers
|
@macos_legacy_timers
|
||||||
when true, false
|
when TrueClass, FalseClass
|
||||||
@macos_legacy_timers = value
|
@macos_legacy_timers = value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -362,36 +375,33 @@ module Homebrew
|
|||||||
"#{HOMEBREW_PREFIX}/bin:#{HOMEBREW_PREFIX}/sbin:/usr/bin:/bin:/usr/sbin:/sbin"
|
"#{HOMEBREW_PREFIX}/bin:#{HOMEBREW_PREFIX}/sbin:/usr/bin:/bin:/usr/sbin:/sbin"
|
||||||
end
|
end
|
||||||
|
|
||||||
sig { returns(T.nilable(T::Array[String])) }
|
sig { returns(T::Array[String]) }
|
||||||
def command
|
def command
|
||||||
@run&.map(&:to_s)&.map { |arg| arg.start_with?("~") ? File.expand_path(arg) : arg }
|
@run.map(&:to_s).map { |arg| arg.start_with?("~") ? File.expand_path(arg) : arg }
|
||||||
end
|
end
|
||||||
|
|
||||||
sig { returns(T::Boolean) }
|
sig { returns(T::Boolean) }
|
||||||
def command?
|
def command?
|
||||||
@run.present?
|
!@run.empty?
|
||||||
end
|
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]
|
|
||||||
sig { returns(String) }
|
sig { returns(String) }
|
||||||
def manual_command
|
def manual_command
|
||||||
vars = @environment_variables.except(:PATH)
|
vars = @environment_variables.except(:PATH)
|
||||||
.map { |k, v| "#{k}=\"#{v}\"" }
|
.map { |k, v| "#{k}=\"#{v}\"" }
|
||||||
|
|
||||||
out = vars + T.must(command).map { |arg| Utils::Shell.sh_quote(arg) } if command?
|
vars.concat(command.map { |arg| Utils::Shell.sh_quote(arg) })
|
||||||
out.join(" ")
|
vars.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a `Boolean` describing if a service is timed.
|
# Returns a `Boolean` describing if a service is timed.
|
||||||
# @return [Boolean]
|
|
||||||
sig { returns(T::Boolean) }
|
sig { returns(T::Boolean) }
|
||||||
def timed?
|
def timed?
|
||||||
@run_type == RUN_TYPE_CRON || @run_type == RUN_TYPE_INTERVAL
|
@run_type == RUN_TYPE_CRON || @run_type == RUN_TYPE_INTERVAL
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a `String` plist.
|
# Returns a `String` plist.
|
||||||
# @return [String]
|
|
||||||
sig { returns(String) }
|
sig { returns(String) }
|
||||||
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
|
||||||
@ -425,7 +435,7 @@ module Homebrew
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if @sockets.present?
|
unless @sockets.empty?
|
||||||
base[:Sockets] = {}
|
base[:Sockets] = {}
|
||||||
@sockets.each do |name, info|
|
@sockets.each do |name, info|
|
||||||
base[:Sockets][name] = {
|
base[:Sockets][name] = {
|
||||||
@ -436,7 +446,7 @@ module Homebrew
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if @cron.present? && @run_type == RUN_TYPE_CRON
|
if !@cron.empty? && @run_type == RUN_TYPE_CRON
|
||||||
base[:StartCalendarInterval] = @cron.reject { |_, value| value == "*" }
|
base[:StartCalendarInterval] = @cron.reject { |_, value| value == "*" }
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -452,12 +462,11 @@ module Homebrew
|
|||||||
end
|
end
|
||||||
|
|
||||||
# Returns a `String` systemd unit.
|
# Returns a `String` systemd unit.
|
||||||
# @return [String]
|
|
||||||
sig { returns(String) }
|
sig { returns(String) }
|
||||||
def to_systemd_unit
|
def to_systemd_unit
|
||||||
# command needs to be first because it initializes all other values
|
# command needs to be first because it initializes all other values
|
||||||
cmd = command&.map { |arg| Utils::Service.systemd_quote(arg) }
|
cmd = command.map { |arg| Utils::Service.systemd_quote(arg) }
|
||||||
&.join(" ")
|
.join(" ")
|
||||||
|
|
||||||
options = []
|
options = []
|
||||||
options << "Type=#{(@launch_only_once == true) ? "oneshot" : "simple"}"
|
options << "Type=#{(@launch_only_once == true) ? "oneshot" : "simple"}"
|
||||||
@ -485,7 +494,6 @@ module Homebrew
|
|||||||
end
|
end
|
||||||
|
|
||||||
# Returns a `String` systemd unit timer.
|
# Returns a `String` systemd unit timer.
|
||||||
# @return [String]
|
|
||||||
sig { returns(String) }
|
sig { returns(String) }
|
||||||
def to_systemd_timer
|
def to_systemd_timer
|
||||||
options = []
|
options = []
|
||||||
@ -512,7 +520,7 @@ module Homebrew
|
|||||||
end
|
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(T::Hash[Symbol, T.untyped]) }
|
||||||
def to_hash
|
def to_hash
|
||||||
name_params = {
|
name_params = {
|
||||||
macos: (plist_name if plist_name != default_plist_name),
|
macos: (plist_name if plist_name != default_plist_name),
|
||||||
@ -521,13 +529,13 @@ module Homebrew
|
|||||||
|
|
||||||
return { name: name_params }.compact_blank if @run_params.blank?
|
return { name: name_params }.compact_blank if @run_params.blank?
|
||||||
|
|
||||||
cron_string = if @cron.present?
|
cron_string = unless @cron.empty?
|
||||||
[:Minute, :Hour, :Day, :Month, :Weekday]
|
[:Minute, :Hour, :Day, :Month, :Weekday]
|
||||||
.map { |key| @cron[key].to_s }
|
.map { |key| @cron[key].to_s }
|
||||||
.join(" ")
|
.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
sockets_var = if @sockets.present?
|
sockets_var = unless @sockets.empty?
|
||||||
@sockets.transform_values { |info| "#{info[:type]}://#{info[:host]}:#{info[:port]}" }
|
@sockets.transform_values { |info| "#{info[:type]}://#{info[:host]}:#{info[:port]}" }
|
||||||
.then do |sockets_hash|
|
.then do |sockets_hash|
|
||||||
# TODO: Remove this code when all users are running on versions of Homebrew
|
# TODO: Remove this code when all users are running on versions of Homebrew
|
||||||
@ -561,11 +569,11 @@ module Homebrew
|
|||||||
process_type: @process_type,
|
process_type: @process_type,
|
||||||
macos_legacy_timers: @macos_legacy_timers,
|
macos_legacy_timers: @macos_legacy_timers,
|
||||||
sockets: sockets_var,
|
sockets: sockets_var,
|
||||||
}.compact
|
}.compact_blank
|
||||||
end
|
end
|
||||||
|
|
||||||
# Turn the service API hash values back into what is expected by the formula DSL.
|
# Turn the service API hash values back into what is expected by the formula DSL.
|
||||||
sig { params(api_hash: Hash).returns(Hash) }
|
sig { params(api_hash: T::Hash[String, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
|
||||||
def self.from_hash(api_hash)
|
def self.from_hash(api_hash)
|
||||||
hash = {}
|
hash = {}
|
||||||
hash[:name] = api_hash["name"].transform_keys(&:to_sym) if api_hash.key?("name")
|
hash[:name] = api_hash["name"].transform_keys(&:to_sym) if api_hash.key?("name")
|
||||||
|
@ -984,7 +984,7 @@ RSpec.describe Homebrew::Service do
|
|||||||
expect(command).to eq(["#{HOMEBREW_PREFIX}/opt/#{name}/bin/beanstalkd", "test"])
|
expect(command).to eq(["#{HOMEBREW_PREFIX}/opt/#{name}/bin/beanstalkd", "test"])
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns nil on Linux", :needs_linux do
|
it "returns empty on Linux", :needs_linux do
|
||||||
f = stub_formula do
|
f = stub_formula do
|
||||||
service do
|
service do
|
||||||
run macos: [opt_bin/"beanstalkd", "test"]
|
run macos: [opt_bin/"beanstalkd", "test"]
|
||||||
@ -993,7 +993,7 @@ RSpec.describe Homebrew::Service do
|
|||||||
end
|
end
|
||||||
|
|
||||||
command = f.service.command
|
command = f.service.command
|
||||||
expect(command).to be_nil
|
expect(command).to be_empty
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns @run data on macOS", :needs_macos do
|
it "returns @run data on macOS", :needs_macos do
|
||||||
@ -1008,7 +1008,7 @@ RSpec.describe Homebrew::Service do
|
|||||||
expect(command).to eq(["#{HOMEBREW_PREFIX}/opt/#{name}/bin/beanstalkd", "test"])
|
expect(command).to eq(["#{HOMEBREW_PREFIX}/opt/#{name}/bin/beanstalkd", "test"])
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns nil on macOS", :needs_macos do
|
it "returns empty on macOS", :needs_macos do
|
||||||
f = stub_formula do
|
f = stub_formula do
|
||||||
service do
|
service do
|
||||||
run linux: [opt_bin/"beanstalkd", "test"]
|
run linux: [opt_bin/"beanstalkd", "test"]
|
||||||
@ -1017,7 +1017,7 @@ RSpec.describe Homebrew::Service do
|
|||||||
end
|
end
|
||||||
|
|
||||||
command = f.service.command
|
command = f.service.command
|
||||||
expect(command).to be_nil
|
expect(command).to be_empty
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns appropriate @run data on Linux", :needs_linux do
|
it "returns appropriate @run data on Linux", :needs_linux do
|
||||||
|
Loading…
x
Reference in New Issue
Block a user