From ef7c0bebbd0c425c1cf118dc13328508371e9dc3 Mon Sep 17 00:00:00 2001 From: Rob Brackett Date: Wed, 26 Jun 2024 15:33:08 -0700 Subject: [PATCH 01/13] Add `watchdog_interval` method and tests This adds a `watchdog_interval` method as well as tests for it and for `watchdog?` (which previously had no tests). I also took the liberty of revising the docs for `watchdog?` to (I hope) be a little clearer. Fixes #6. --- lib/sd_notify.rb | 38 ++++++++++++++++++++++++-------------- test/sd_notify_test.rb | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 14 deletions(-) diff --git a/lib/sd_notify.rb b/lib/sd_notify.rb index 66d73ed..cce66ba 100644 --- a/lib/sd_notify.rb +++ b/lib/sd_notify.rb @@ -59,9 +59,13 @@ def self.fdstore(unset_env=false) notify(FDSTORE, unset_env) end - # If the $WATCHDOG_USEC environment variable is set, - # and the $WATCHDOG_PID variable is unset or set to the PID of the current - # process + # Determine whether the systemd's watchdog is enabled for your program. If + # enabled, systemd will restart (or take some configured action) on your + # service when it goes N seconds without receiving a `WATCHDOG` notification + # from your program. + # + # See #watchdog for sending notifications and #watchdog_interval for how + # frequently to send them. # # @return [Boolean] true if the service manager expects watchdog keep-alive # notification messages to be sent from this process. @@ -69,23 +73,29 @@ def self.fdstore(unset_env=false) # @note Unlike sd_watchdog_enabled(3), this method does not mutate the # environment. def self.watchdog? - wd_usec = ENV["WATCHDOG_USEC"] - wd_pid = ENV["WATCHDOG_PID"] - - return false if !wd_usec - - begin - wd_usec = Integer(wd_usec) - rescue - return false - end + return false if watchdog_interval == 0.0 - return false if wd_usec <= 0 + wd_pid = ENV["WATCHDOG_PID"] return true if !wd_pid || wd_pid == $$.to_s false end + # Get the expected number of seconds between watchdog notifications. If + # systemd's watchdog manager is enabled, it will take action if it does not + # receive notifications at least this often from your program. + # + # @return [Float] the frequency (in seconds) at which the service manager + # expects watchdog keep-alive notification messages from this process. + # + # @note Unlike sd_watchdog_enabled(3), this returns seconds, not microseconds. + def self.watchdog_interval + wd_usec = Integer(ENV["WATCHDOG_USEC"]) + wd_usec.positive? ? wd_usec / 1e6 : 0.0 + rescue StandardError + 0.0 + end + # Notify systemd with the provided state, via the notification socket, if # any. # diff --git a/test/sd_notify_test.rb b/test/sd_notify_test.rb index 535fb39..3254534 100644 --- a/test/sd_notify_test.rb +++ b/test/sd_notify_test.rb @@ -30,11 +30,50 @@ def test_sd_notify_ready_unset assert_nil(ENV["NOTIFY_SOCKET"]) end + def test_sd_notify_watchdog_disabled + setup_socket + + assert_equal(false, SdNotify.watchdog?) + end + + def test_sd_notify_watchdog_enabled + ENV["WATCHDOG_USEC"] = "5_000_000" + ENV["WATCHDOG_PID"] = $$.to_s + setup_socket + + assert_equal(true, SdNotify.watchdog?) + end + + def test_sd_notify_watchdog_enabled_for_a_different_process + ENV["WATCHDOG_USEC"] = "5_000_000" + ENV["WATCHDOG_PID"] = ($$ + 1).to_s + setup_socket + + assert_equal(false, SdNotify.watchdog?) + end + + def test_sd_notify_watchdog_interval_disabled + setup_socket + + assert_equal(0.0, SdNotify.watchdog_interval) + end + + def test_sd_notify_watchdog_interval_enabled + ENV["WATCHDOG_USEC"] = "5_000_000" + ENV["WATCHDOG_PID"] = $$.to_s + setup_socket + + assert_equal(5.0, SdNotify.watchdog_interval) + end + def teardown @socket.close if @socket File.unlink(@sockaddr) if @sockaddr @socket = nil @sockaddr = nil + ENV.delete("NOTIFY_SOCKET") + ENV.delete("WATCHDOG_USEC") + ENV.delete("WATCHDOG_PID") end private From 131eaee014224c05713f33c0f23cc13653b0b279 Mon Sep 17 00:00:00 2001 From: Rob Brackett Date: Wed, 26 Jun 2024 15:34:49 -0700 Subject: [PATCH 02/13] Add watchdog example to README --- README.md | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/README.md b/README.md index b0b239f..77f1a16 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,58 @@ sleep 2 puts "Bye" ``` +If you are operating a long-running program and want to use systemd's watchdog service manager to monitor your program: + +```ruby +require "sd_notify" + +puts "Hello! Booting..." + +# doing some initialization work... +sleep 2 + +# notify systemd that we're ready +SdNotify.ready + +# You might have a more complicated method of keeping an eye on the internal +# health of your program, although you will usually want to do this on a +# separate thread so notifications are not held up by especially long chunks of +# work in your main working thread. +watchdog_thread = if SdNotify.watchdog? + Thread.new do + loop do + # Systemd recommends pinging the watchdog at half the configured interval + # to make sure notifications always arrive in time. + sleep SdNotify.watchdog_interval / 2 + if service_is_healthy + SdNotify.watchdog + else + break + end + end +end + +# Do our main work... +loop do + sleep 10 + sum += 1 + break +end + +puts "Finished working. Shutting down..." + +# Stop watchdog +watchdog_thread.exit + +# notify systemd we're shutting down +SdNotify.stopping + +# doing some cleanup work... +sleep 2 + +puts "Bye" +``` + ## License ruby-sdnotify is licensed under MIT. See [LICENSE](LICENSE). From 4644653e8ae529a814411c1a0ae1c62b32ca38e4 Mon Sep 17 00:00:00 2001 From: Rob Brackett Date: Wed, 26 Jun 2024 15:35:06 -0700 Subject: [PATCH 03/13] Update Rubocop config for current Rubocop Running Rubocop printed out a huge number of new warnings and errors. For the most part, I just silenced them since this gem has been pretty stable and I assume you don't want to modify things just to match newer recommendations. I did add `required_ruby_version` to the gemspec, though, since that just seems like a good idea. It's set to 2.3.0, based on the GitHub Actions CI job. --- .rubocop.yml | 378 ++++++++++++++++++++++++++++++++++++++++++++++ sd_notify.gemspec | 1 + 2 files changed, 379 insertions(+) diff --git a/.rubocop.yml b/.rubocop.yml index 9b7fff4..f0e8684 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,6 +1,9 @@ require: - rubocop-performance +AllCops: + TargetRubyVersion: 2.3 + Layout/SpaceAroundEqualsInParameterDefault: EnforcedStyle: no_space @@ -12,3 +15,378 @@ Style/SafeNavigation: Style/NegatedIf: Enabled: false + +Metrics/MethodLength: + Max: 20 + +Style/OptionalBooleanParameter: + Enabled: false + +Style/SpecialGlobalVars: + Enabled: false + +Gemspec/DeprecatedAttributeAssignment: # new in 1.30 + Enabled: false + +Gemspec/DevelopmentDependencies: # new in 1.44 + Enabled: false + +Gemspec/RequireMFA: # new in 1.23 + Enabled: false + +Layout/LineContinuationLeadingSpace: # new in 1.31 + Enabled: false + +Layout/LineContinuationSpacing: # new in 1.31 + Enabled: false + +Layout/LineEndStringConcatenationIndentation: # new in 1.18 + Enabled: false + +Layout/SpaceBeforeBrackets: # new in 1.7 + Enabled: false + +Lint/AmbiguousAssignment: # new in 1.7 + Enabled: false + +Lint/AmbiguousOperatorPrecedence: # new in 1.21 + Enabled: false + +Lint/AmbiguousRange: # new in 1.19 + Enabled: false + +Lint/ConstantOverwrittenInRescue: # new in 1.31 + Enabled: false + +Lint/DeprecatedConstants: # new in 1.8 + Enabled: false + +Lint/DuplicateBranch: # new in 1.3 + Enabled: false + +Lint/DuplicateMagicComment: # new in 1.37 + Enabled: false + +Lint/DuplicateMatchPattern: # new in 1.50 + Enabled: false + +Lint/DuplicateRegexpCharacterClassElement: # new in 1.1 + Enabled: false + +Lint/EmptyBlock: # new in 1.1 + Enabled: false + +Lint/EmptyClass: # new in 1.3 + Enabled: false + +Lint/EmptyInPattern: # new in 1.16 + Enabled: false + +Lint/IncompatibleIoSelectWithFiberScheduler: # new in 1.21 + Enabled: false + +Lint/ItWithoutArgumentsInBlock: # new in 1.59 + Enabled: false + +Lint/LambdaWithoutLiteralBlock: # new in 1.8 + Enabled: false + +Lint/LiteralAssignmentInCondition: # new in 1.58 + Enabled: false + +Lint/MixedCaseRange: # new in 1.53 + Enabled: false + +Lint/NoReturnInBeginEndBlocks: # new in 1.2 + Enabled: false + +Lint/NonAtomicFileOperation: # new in 1.31 + Enabled: false + +Lint/NumberedParameterAssignment: # new in 1.9 + Enabled: false + +Lint/OrAssignmentToConstant: # new in 1.9 + Enabled: false + +Lint/RedundantDirGlobSort: # new in 1.8 + Enabled: false + +Lint/RedundantRegexpQuantifiers: # new in 1.53 + Enabled: false + +Lint/RefinementImportMethods: # new in 1.27 + Enabled: false + +Lint/RequireRangeParentheses: # new in 1.32 + Enabled: false + +Lint/RequireRelativeSelfPath: # new in 1.22 + Enabled: false + +Lint/SymbolConversion: # new in 1.9 + Enabled: false + +Lint/ToEnumArguments: # new in 1.1 + Enabled: false + +Lint/TripleQuotes: # new in 1.9 + Enabled: false + +Lint/UnexpectedBlockArity: # new in 1.5 + Enabled: false + +Lint/UnmodifiedReduceAccumulator: # new in 1.1 + Enabled: false + +Lint/UselessRescue: # new in 1.43 + Enabled: false + +Lint/UselessRuby2Keywords: # new in 1.23 + Enabled: false + +Metrics/CollectionLiteralLength: # new in 1.47 + Enabled: false + +Naming/BlockForwarding: # new in 1.24 + Enabled: false + +Security/CompoundHash: # new in 1.28 + Enabled: false + +Security/IoMethods: # new in 1.22 + Enabled: false + +Style/ArgumentsForwarding: # new in 1.1 + Enabled: false + +Style/ArrayIntersect: # new in 1.40 + Enabled: false + +Style/CollectionCompact: # new in 1.2 + Enabled: false + +Style/ComparableClamp: # new in 1.44 + Enabled: false + +Style/ConcatArrayLiterals: # new in 1.41 + Enabled: false + +Style/DataInheritance: # new in 1.49 + Enabled: false + +Style/DirEmpty: # new in 1.48 + Enabled: false + +Style/DocumentDynamicEvalDefinition: # new in 1.1 + Enabled: false + +Style/EmptyHeredoc: # new in 1.32 + Enabled: false + +Style/EndlessMethod: # new in 1.8 + Enabled: false + +Style/EnvHome: # new in 1.29 + Enabled: false + +Style/ExactRegexpMatch: # new in 1.51 + Enabled: false + +Style/FetchEnvVar: # new in 1.28 + Enabled: false + +Style/FileEmpty: # new in 1.48 + Enabled: false + +Style/FileRead: # new in 1.24 + Enabled: false + +Style/FileWrite: # new in 1.24 + Enabled: false + +Style/HashConversion: # new in 1.10 + Enabled: false + +Style/HashExcept: # new in 1.7 + Enabled: false + +Style/IfWithBooleanLiteralBranches: # new in 1.9 + Enabled: false + +Style/InPatternThen: # new in 1.16 + Enabled: false + +Style/MagicCommentFormat: # new in 1.35 + Enabled: false + +Style/MapCompactWithConditionalBlock: # new in 1.30 + Enabled: false + +Style/MapIntoArray: # new in 1.63 + Enabled: false + +Style/MapToHash: # new in 1.24 + Enabled: false + +Style/MapToSet: # new in 1.42 + Enabled: false + +Style/MinMaxComparison: # new in 1.42 + Enabled: false + +Style/MultilineInPatternThen: # new in 1.16 + Enabled: false + +Style/NegatedIfElseCondition: # new in 1.2 + Enabled: false + +Style/NestedFileDirname: # new in 1.26 + Enabled: false + +Style/NilLambda: # new in 1.3 + Enabled: false + +Style/NumberedParameters: # new in 1.22 + Enabled: false + +Style/NumberedParametersLimit: # new in 1.22 + Enabled: false + +Style/ObjectThen: # new in 1.28 + Enabled: false + +Style/OpenStructUse: # new in 1.23 + Enabled: false + +Style/OperatorMethodCall: # new in 1.37 + Enabled: false + +Style/QuotedSymbols: # new in 1.16 + Enabled: false + +Style/RedundantArgument: # new in 1.4 + Enabled: false + +Style/RedundantArrayConstructor: # new in 1.52 + Enabled: false + +Style/RedundantConstantBase: # new in 1.40 + Enabled: false + +Style/RedundantCurrentDirectoryInPath: # new in 1.53 + Enabled: false + +Style/RedundantDoubleSplatHashBraces: # new in 1.41 + Enabled: false + +Style/RedundantEach: # new in 1.38 + Enabled: false + +Style/RedundantFilterChain: # new in 1.52 + Enabled: false + +Style/RedundantHeredocDelimiterQuotes: # new in 1.45 + Enabled: false + +Style/RedundantInitialize: # new in 1.27 + Enabled: false + +Style/RedundantLineContinuation: # new in 1.49 + Enabled: false + +Style/RedundantRegexpArgument: # new in 1.53 + Enabled: false + +Style/RedundantRegexpConstructor: # new in 1.52 + Enabled: false + +Style/RedundantSelfAssignmentBranch: # new in 1.19 + Enabled: false + +Style/RedundantStringEscape: # new in 1.37 + Enabled: false + +Style/ReturnNilInPredicateMethodDefinition: # new in 1.53 + Enabled: false + +Style/SelectByRegexp: # new in 1.22 + Enabled: false + +Style/SendWithLiteralMethodName: # new in 1.64 + Enabled: false + +Style/SingleLineDoEndBlock: # new in 1.57 + Enabled: false + +Style/StringChars: # new in 1.12 + Enabled: false + +Style/SuperArguments: # new in 1.64 + Enabled: false + +Style/SuperWithArgsParentheses: # new in 1.58 + Enabled: false + +Style/SwapValues: # new in 1.1 + Enabled: false + +Style/YAMLFileRead: # new in 1.53 + Enabled: false + +Performance/AncestorsInclude: # new in 1.7 + Enabled: false + +Performance/BigDecimalWithNumericArgument: # new in 1.7 + Enabled: false + +Performance/BlockGivenWithExplicitBlock: # new in 1.9 + Enabled: false + +Performance/CollectionLiteralInLoop: # new in 1.8 + Enabled: false + +Performance/ConcurrentMonotonicTime: # new in 1.12 + Enabled: false + +Performance/ConstantRegexp: # new in 1.9 + Enabled: false + +Performance/MapCompact: # new in 1.11 + Enabled: false + +Performance/MapMethodChain: # new in 1.19 + Enabled: false + +Performance/MethodObjectAsBlock: # new in 1.9 + Enabled: false + +Performance/RedundantEqualityComparisonBlock: # new in 1.10 + Enabled: false + +Performance/RedundantSortBlock: # new in 1.7 + Enabled: false + +Performance/RedundantSplitRegexpArgument: # new in 1.10 + Enabled: false + +Performance/RedundantStringChars: # new in 1.7 + Enabled: false + +Performance/ReverseFirst: # new in 1.7 + Enabled: false + +Performance/SortReverse: # new in 1.7 + Enabled: false + +Performance/Squeeze: # new in 1.7 + Enabled: false + +Performance/StringIdentifierArgument: # new in 1.13 + Enabled: false + +Performance/StringInclude: # new in 1.7 + Enabled: false + +Performance/Sum: # new in 1.8 + Enabled: false diff --git a/sd_notify.gemspec b/sd_notify.gemspec index 46dae4f..113ceb2 100644 --- a/sd_notify.gemspec +++ b/sd_notify.gemspec @@ -11,6 +11,7 @@ Gem::Specification.new do |s| s.files = ["lib/sd_notify.rb", "LICENSE", "README.md", "CHANGELOG.md"] s.homepage = "https://github.com/agis/ruby-sdnotify" s.license = "MIT" + s.required_ruby_version = ">= 2.3.0" s.add_development_dependency "minitest" s.add_development_dependency "rubocop" From 744ac7d71a94fb77264e7b8706cef1e3a771684c Mon Sep 17 00:00:00 2001 From: Rob Brackett Date: Wed, 26 Jun 2024 15:52:30 -0700 Subject: [PATCH 04/13] Oops, I missed the separate Ruby 2.2 job --- .rubocop.yml | 2 +- sd_notify.gemspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index f0e8684..6f3f6f2 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -2,7 +2,7 @@ require: - rubocop-performance AllCops: - TargetRubyVersion: 2.3 + TargetRubyVersion: 2.2 Layout/SpaceAroundEqualsInParameterDefault: EnforcedStyle: no_space diff --git a/sd_notify.gemspec b/sd_notify.gemspec index 113ceb2..cf44be0 100644 --- a/sd_notify.gemspec +++ b/sd_notify.gemspec @@ -11,7 +11,7 @@ Gem::Specification.new do |s| s.files = ["lib/sd_notify.rb", "LICENSE", "README.md", "CHANGELOG.md"] s.homepage = "https://github.com/agis/ruby-sdnotify" s.license = "MIT" - s.required_ruby_version = ">= 2.3.0" + s.required_ruby_version = ">= 2.2.0" s.add_development_dependency "minitest" s.add_development_dependency "rubocop" From 22940b4a01a916a7b07e58dcf0cf660bfa7bdfee Mon Sep 17 00:00:00 2001 From: Rob Brackett Date: Wed, 26 Jun 2024 15:56:07 -0700 Subject: [PATCH 05/13] `positive?` does not exist in Ruby 2.2 --- lib/sd_notify.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sd_notify.rb b/lib/sd_notify.rb index cce66ba..7d4f087 100644 --- a/lib/sd_notify.rb +++ b/lib/sd_notify.rb @@ -91,7 +91,7 @@ def self.watchdog? # @note Unlike sd_watchdog_enabled(3), this returns seconds, not microseconds. def self.watchdog_interval wd_usec = Integer(ENV["WATCHDOG_USEC"]) - wd_usec.positive? ? wd_usec / 1e6 : 0.0 + wd_usec > 0 ? wd_usec / 1e6 : 0.0 rescue StandardError 0.0 end From f7901d71bfcb11b0a799f73178aae53c7d93fad8 Mon Sep 17 00:00:00 2001 From: Rob Brackett Date: Sun, 19 Oct 2025 21:46:13 -0700 Subject: [PATCH 06/13] Remove Rubocop, per review The existing Rubocop config is set up for an unknown version of Rubocop from several years ago that was never pinned. The current rules Rubocop suggests are undesired, so this just removes it instead. --- .rubocop.yml | 392 ---------------------------------------------- sd_notify.gemspec | 3 - 2 files changed, 395 deletions(-) delete mode 100644 .rubocop.yml diff --git a/.rubocop.yml b/.rubocop.yml deleted file mode 100644 index 6f3f6f2..0000000 --- a/.rubocop.yml +++ /dev/null @@ -1,392 +0,0 @@ -require: - - rubocop-performance - -AllCops: - TargetRubyVersion: 2.2 - -Layout/SpaceAroundEqualsInParameterDefault: - EnforcedStyle: no_space - -Style/StringLiterals: - EnforcedStyle: double_quotes - -Style/SafeNavigation: - Enabled: false - -Style/NegatedIf: - Enabled: false - -Metrics/MethodLength: - Max: 20 - -Style/OptionalBooleanParameter: - Enabled: false - -Style/SpecialGlobalVars: - Enabled: false - -Gemspec/DeprecatedAttributeAssignment: # new in 1.30 - Enabled: false - -Gemspec/DevelopmentDependencies: # new in 1.44 - Enabled: false - -Gemspec/RequireMFA: # new in 1.23 - Enabled: false - -Layout/LineContinuationLeadingSpace: # new in 1.31 - Enabled: false - -Layout/LineContinuationSpacing: # new in 1.31 - Enabled: false - -Layout/LineEndStringConcatenationIndentation: # new in 1.18 - Enabled: false - -Layout/SpaceBeforeBrackets: # new in 1.7 - Enabled: false - -Lint/AmbiguousAssignment: # new in 1.7 - Enabled: false - -Lint/AmbiguousOperatorPrecedence: # new in 1.21 - Enabled: false - -Lint/AmbiguousRange: # new in 1.19 - Enabled: false - -Lint/ConstantOverwrittenInRescue: # new in 1.31 - Enabled: false - -Lint/DeprecatedConstants: # new in 1.8 - Enabled: false - -Lint/DuplicateBranch: # new in 1.3 - Enabled: false - -Lint/DuplicateMagicComment: # new in 1.37 - Enabled: false - -Lint/DuplicateMatchPattern: # new in 1.50 - Enabled: false - -Lint/DuplicateRegexpCharacterClassElement: # new in 1.1 - Enabled: false - -Lint/EmptyBlock: # new in 1.1 - Enabled: false - -Lint/EmptyClass: # new in 1.3 - Enabled: false - -Lint/EmptyInPattern: # new in 1.16 - Enabled: false - -Lint/IncompatibleIoSelectWithFiberScheduler: # new in 1.21 - Enabled: false - -Lint/ItWithoutArgumentsInBlock: # new in 1.59 - Enabled: false - -Lint/LambdaWithoutLiteralBlock: # new in 1.8 - Enabled: false - -Lint/LiteralAssignmentInCondition: # new in 1.58 - Enabled: false - -Lint/MixedCaseRange: # new in 1.53 - Enabled: false - -Lint/NoReturnInBeginEndBlocks: # new in 1.2 - Enabled: false - -Lint/NonAtomicFileOperation: # new in 1.31 - Enabled: false - -Lint/NumberedParameterAssignment: # new in 1.9 - Enabled: false - -Lint/OrAssignmentToConstant: # new in 1.9 - Enabled: false - -Lint/RedundantDirGlobSort: # new in 1.8 - Enabled: false - -Lint/RedundantRegexpQuantifiers: # new in 1.53 - Enabled: false - -Lint/RefinementImportMethods: # new in 1.27 - Enabled: false - -Lint/RequireRangeParentheses: # new in 1.32 - Enabled: false - -Lint/RequireRelativeSelfPath: # new in 1.22 - Enabled: false - -Lint/SymbolConversion: # new in 1.9 - Enabled: false - -Lint/ToEnumArguments: # new in 1.1 - Enabled: false - -Lint/TripleQuotes: # new in 1.9 - Enabled: false - -Lint/UnexpectedBlockArity: # new in 1.5 - Enabled: false - -Lint/UnmodifiedReduceAccumulator: # new in 1.1 - Enabled: false - -Lint/UselessRescue: # new in 1.43 - Enabled: false - -Lint/UselessRuby2Keywords: # new in 1.23 - Enabled: false - -Metrics/CollectionLiteralLength: # new in 1.47 - Enabled: false - -Naming/BlockForwarding: # new in 1.24 - Enabled: false - -Security/CompoundHash: # new in 1.28 - Enabled: false - -Security/IoMethods: # new in 1.22 - Enabled: false - -Style/ArgumentsForwarding: # new in 1.1 - Enabled: false - -Style/ArrayIntersect: # new in 1.40 - Enabled: false - -Style/CollectionCompact: # new in 1.2 - Enabled: false - -Style/ComparableClamp: # new in 1.44 - Enabled: false - -Style/ConcatArrayLiterals: # new in 1.41 - Enabled: false - -Style/DataInheritance: # new in 1.49 - Enabled: false - -Style/DirEmpty: # new in 1.48 - Enabled: false - -Style/DocumentDynamicEvalDefinition: # new in 1.1 - Enabled: false - -Style/EmptyHeredoc: # new in 1.32 - Enabled: false - -Style/EndlessMethod: # new in 1.8 - Enabled: false - -Style/EnvHome: # new in 1.29 - Enabled: false - -Style/ExactRegexpMatch: # new in 1.51 - Enabled: false - -Style/FetchEnvVar: # new in 1.28 - Enabled: false - -Style/FileEmpty: # new in 1.48 - Enabled: false - -Style/FileRead: # new in 1.24 - Enabled: false - -Style/FileWrite: # new in 1.24 - Enabled: false - -Style/HashConversion: # new in 1.10 - Enabled: false - -Style/HashExcept: # new in 1.7 - Enabled: false - -Style/IfWithBooleanLiteralBranches: # new in 1.9 - Enabled: false - -Style/InPatternThen: # new in 1.16 - Enabled: false - -Style/MagicCommentFormat: # new in 1.35 - Enabled: false - -Style/MapCompactWithConditionalBlock: # new in 1.30 - Enabled: false - -Style/MapIntoArray: # new in 1.63 - Enabled: false - -Style/MapToHash: # new in 1.24 - Enabled: false - -Style/MapToSet: # new in 1.42 - Enabled: false - -Style/MinMaxComparison: # new in 1.42 - Enabled: false - -Style/MultilineInPatternThen: # new in 1.16 - Enabled: false - -Style/NegatedIfElseCondition: # new in 1.2 - Enabled: false - -Style/NestedFileDirname: # new in 1.26 - Enabled: false - -Style/NilLambda: # new in 1.3 - Enabled: false - -Style/NumberedParameters: # new in 1.22 - Enabled: false - -Style/NumberedParametersLimit: # new in 1.22 - Enabled: false - -Style/ObjectThen: # new in 1.28 - Enabled: false - -Style/OpenStructUse: # new in 1.23 - Enabled: false - -Style/OperatorMethodCall: # new in 1.37 - Enabled: false - -Style/QuotedSymbols: # new in 1.16 - Enabled: false - -Style/RedundantArgument: # new in 1.4 - Enabled: false - -Style/RedundantArrayConstructor: # new in 1.52 - Enabled: false - -Style/RedundantConstantBase: # new in 1.40 - Enabled: false - -Style/RedundantCurrentDirectoryInPath: # new in 1.53 - Enabled: false - -Style/RedundantDoubleSplatHashBraces: # new in 1.41 - Enabled: false - -Style/RedundantEach: # new in 1.38 - Enabled: false - -Style/RedundantFilterChain: # new in 1.52 - Enabled: false - -Style/RedundantHeredocDelimiterQuotes: # new in 1.45 - Enabled: false - -Style/RedundantInitialize: # new in 1.27 - Enabled: false - -Style/RedundantLineContinuation: # new in 1.49 - Enabled: false - -Style/RedundantRegexpArgument: # new in 1.53 - Enabled: false - -Style/RedundantRegexpConstructor: # new in 1.52 - Enabled: false - -Style/RedundantSelfAssignmentBranch: # new in 1.19 - Enabled: false - -Style/RedundantStringEscape: # new in 1.37 - Enabled: false - -Style/ReturnNilInPredicateMethodDefinition: # new in 1.53 - Enabled: false - -Style/SelectByRegexp: # new in 1.22 - Enabled: false - -Style/SendWithLiteralMethodName: # new in 1.64 - Enabled: false - -Style/SingleLineDoEndBlock: # new in 1.57 - Enabled: false - -Style/StringChars: # new in 1.12 - Enabled: false - -Style/SuperArguments: # new in 1.64 - Enabled: false - -Style/SuperWithArgsParentheses: # new in 1.58 - Enabled: false - -Style/SwapValues: # new in 1.1 - Enabled: false - -Style/YAMLFileRead: # new in 1.53 - Enabled: false - -Performance/AncestorsInclude: # new in 1.7 - Enabled: false - -Performance/BigDecimalWithNumericArgument: # new in 1.7 - Enabled: false - -Performance/BlockGivenWithExplicitBlock: # new in 1.9 - Enabled: false - -Performance/CollectionLiteralInLoop: # new in 1.8 - Enabled: false - -Performance/ConcurrentMonotonicTime: # new in 1.12 - Enabled: false - -Performance/ConstantRegexp: # new in 1.9 - Enabled: false - -Performance/MapCompact: # new in 1.11 - Enabled: false - -Performance/MapMethodChain: # new in 1.19 - Enabled: false - -Performance/MethodObjectAsBlock: # new in 1.9 - Enabled: false - -Performance/RedundantEqualityComparisonBlock: # new in 1.10 - Enabled: false - -Performance/RedundantSortBlock: # new in 1.7 - Enabled: false - -Performance/RedundantSplitRegexpArgument: # new in 1.10 - Enabled: false - -Performance/RedundantStringChars: # new in 1.7 - Enabled: false - -Performance/ReverseFirst: # new in 1.7 - Enabled: false - -Performance/SortReverse: # new in 1.7 - Enabled: false - -Performance/Squeeze: # new in 1.7 - Enabled: false - -Performance/StringIdentifierArgument: # new in 1.13 - Enabled: false - -Performance/StringInclude: # new in 1.7 - Enabled: false - -Performance/Sum: # new in 1.8 - Enabled: false diff --git a/sd_notify.gemspec b/sd_notify.gemspec index cf44be0..5690583 100644 --- a/sd_notify.gemspec +++ b/sd_notify.gemspec @@ -11,9 +11,6 @@ Gem::Specification.new do |s| s.files = ["lib/sd_notify.rb", "LICENSE", "README.md", "CHANGELOG.md"] s.homepage = "https://github.com/agis/ruby-sdnotify" s.license = "MIT" - s.required_ruby_version = ">= 2.2.0" s.add_development_dependency "minitest" - s.add_development_dependency "rubocop" - s.add_development_dependency "rubocop-performance" end From 57b22fbab49c2a0ff790d4dc2a40bba49816a2ab Mon Sep 17 00:00:00 2001 From: Rob Brackett Date: Sun, 19 Oct 2025 21:55:36 -0700 Subject: [PATCH 07/13] Return `nil` if no interval is set, per review --- lib/sd_notify.rb | 19 ++++++++++--------- test/sd_notify_test.rb | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/lib/sd_notify.rb b/lib/sd_notify.rb index 7d4f087..7c82480 100644 --- a/lib/sd_notify.rb +++ b/lib/sd_notify.rb @@ -73,7 +73,7 @@ def self.fdstore(unset_env=false) # @note Unlike sd_watchdog_enabled(3), this method does not mutate the # environment. def self.watchdog? - return false if watchdog_interval == 0.0 + return false unless watchdog_interval wd_pid = ENV["WATCHDOG_PID"] return true if !wd_pid || wd_pid == $$.to_s @@ -83,17 +83,18 @@ def self.watchdog? # Get the expected number of seconds between watchdog notifications. If # systemd's watchdog manager is enabled, it will take action if it does not - # receive notifications at least this often from your program. + # receive notifications at least this often from your program. Returns +nil+ + # if no watchdog interval is set. # - # @return [Float] the frequency (in seconds) at which the service manager - # expects watchdog keep-alive notification messages from this process. + # @return [Float, nil] The frequency (in seconds) at which the service + # manager expects watchdog keep-alive notification messages from this + # process. # - # @note Unlike sd_watchdog_enabled(3), this returns seconds, not microseconds. + # @note Unlike +sd_watchdog_enabled(3)+, this returns seconds, not + # microseconds. def self.watchdog_interval - wd_usec = Integer(ENV["WATCHDOG_USEC"]) - wd_usec > 0 ? wd_usec / 1e6 : 0.0 - rescue StandardError - 0.0 + wd_usec = Integer(ENV["WATCHDOG_USEC"]) rescue 0 + wd_usec > 0 ? wd_usec / 1e6 : nil end # Notify systemd with the provided state, via the notification socket, if diff --git a/test/sd_notify_test.rb b/test/sd_notify_test.rb index 3254534..d90c730 100644 --- a/test/sd_notify_test.rb +++ b/test/sd_notify_test.rb @@ -55,7 +55,7 @@ def test_sd_notify_watchdog_enabled_for_a_different_process def test_sd_notify_watchdog_interval_disabled setup_socket - assert_equal(0.0, SdNotify.watchdog_interval) + assert_nil(SdNotify.watchdog_interval) end def test_sd_notify_watchdog_interval_enabled From effcca97821bf1ed1e79a43a512e5a19c44a09c8 Mon Sep 17 00:00:00 2001 From: Rob Brackett Date: Sun, 19 Oct 2025 22:01:11 -0700 Subject: [PATCH 08/13] Test truthiness, not boolean-ness, per review --- test/sd_notify_test.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/sd_notify_test.rb b/test/sd_notify_test.rb index d90c730..a46065f 100644 --- a/test/sd_notify_test.rb +++ b/test/sd_notify_test.rb @@ -33,7 +33,7 @@ def test_sd_notify_ready_unset def test_sd_notify_watchdog_disabled setup_socket - assert_equal(false, SdNotify.watchdog?) + refute(SdNotify.watchdog?) end def test_sd_notify_watchdog_enabled @@ -41,7 +41,7 @@ def test_sd_notify_watchdog_enabled ENV["WATCHDOG_PID"] = $$.to_s setup_socket - assert_equal(true, SdNotify.watchdog?) + assert(SdNotify.watchdog?) end def test_sd_notify_watchdog_enabled_for_a_different_process @@ -49,7 +49,7 @@ def test_sd_notify_watchdog_enabled_for_a_different_process ENV["WATCHDOG_PID"] = ($$ + 1).to_s setup_socket - assert_equal(false, SdNotify.watchdog?) + refute(SdNotify.watchdog?) end def test_sd_notify_watchdog_interval_disabled From 312c26fda977affda1da7653e495adb4b55bf9fe Mon Sep 17 00:00:00 2001 From: Rob Brackett Date: Sun, 19 Oct 2025 22:04:30 -0700 Subject: [PATCH 09/13] Fix typo in README example --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 77f1a16..10a12c7 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,7 @@ watchdog_thread = if SdNotify.watchdog? SdNotify.watchdog else break + end end end end From 2c4dd9c3337b26f99969bdf3386d00ac54e547c5 Mon Sep 17 00:00:00 2001 From: Rob Brackett Date: Sun, 19 Oct 2025 22:16:25 -0700 Subject: [PATCH 10/13] Use supported runner for old Ruby, test current Ruby --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d8773c9..3c18afa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,7 @@ on: jobs: build_older: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 name: Ruby ${{ matrix.ruby }} strategy: matrix: @@ -40,6 +40,8 @@ jobs: - '3.0' - '3.1' - '3.2' + - '3.3' + - '3.4' steps: - uses: actions/checkout@v3 From 08014b54d54e32a607fac319b8ed0562a44e54ed Mon Sep 17 00:00:00 2001 From: Rob Brackett Date: Sun, 19 Oct 2025 22:25:47 -0700 Subject: [PATCH 11/13] It turns out we can not longer test on Ruby 2.2 The `ruby/setup-ruby` action does not support Ruby 2.2 on Ubuntu 22+ (see https://github.com/ruby/setup-ruby/issues/496) and GitHub Actions no longer supports older versions of Ubuntu, so we can no longer test Ruby 2.2 (at least not without a lot of extra work!). --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3c18afa..7c7d8c1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: strategy: matrix: ruby: - - 2.2 + - 2.1 steps: - uses: actions/checkout@v3 @@ -32,6 +32,7 @@ jobs: strategy: matrix: ruby: + # Ruby 2.2 is not supported on Ubuntu 22+ https://github.com/ruby/setup-ruby/issues/496 - 2.3 - 2.4 - 2.5 From 9ff9cdc0c89690e199efaf278fd47ae9be6628c9 Mon Sep 17 00:00:00 2001 From: Rob Brackett Date: Sun, 19 Oct 2025 22:42:30 -0700 Subject: [PATCH 12/13] Try an older Minitest version --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c7d8c1..7238aa3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,6 +23,8 @@ jobs: with: ruby-version: ${{ matrix.ruby }} bundler-cache: true + - name: Install compatible Minitest + run: bundle add minitest --version 5.0.8 - name: Run tests run: ruby -Ilib:test test/* From 20b1483e631842280e34e58d12e1dcbb3ea871e7 Mon Sep 17 00:00:00 2001 From: Rob Brackett Date: Sun, 19 Oct 2025 23:08:28 -0700 Subject: [PATCH 13/13] Fix test on Ruby 2.1 --- .github/workflows/ci.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7238aa3..1071d53 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,10 +23,8 @@ jobs: with: ruby-version: ${{ matrix.ruby }} bundler-cache: true - - name: Install compatible Minitest - run: bundle add minitest --version 5.0.8 - name: Run tests - run: ruby -Ilib:test test/* + run: bundle exec ruby -Ilib:test test/* build: runs-on: ubuntu-latest