From 8cd942be2d7b1296ba190b34bb09221a6fcd0093 Mon Sep 17 00:00:00 2001 From: Snowflake Date: Mon, 8 Jun 2026 11:47:00 +0800 Subject: [PATCH] =?UTF-8?q?release:=20=E5=8F=91=E5=B8=83=E7=89=88=E6=9C=AC?= =?UTF-8?q?=2060176fa=EF=BC=8CRuboCop=20=E9=9B=B6=E5=91=8A=E8=AD=A6?= =?UTF-8?q?=EF=BC=8C=E7=BD=91=E9=A1=B5=E6=96=B0=E5=A2=9E=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E5=8F=B7=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .rubocop.yml | 6 ++ APP_VERSIONS_LIST | 3 +- OfficialWebsite/index.html | 3 + config.rb | 2 +- main.rb | 78 ++++++++++++++----------- method_executor.rb | 114 +++++++++++++++++++------------------ server_status_detector.rb | 55 +++++++++--------- systemd_notifier.rb | 28 ++++----- updates.rb | 22 ++++--- 9 files changed, 171 insertions(+), 140 deletions(-) create mode 100644 .rubocop.yml diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..62022c8 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,6 @@ +AllCops: + NewCops: enable +Layout/EndOfLine: + Enabled: false +Style/Documentation: + Enabled: false diff --git a/APP_VERSIONS_LIST b/APP_VERSIONS_LIST index f76f4c1..55c539a 100644 --- a/APP_VERSIONS_LIST +++ b/APP_VERSIONS_LIST @@ -1 +1,2 @@ -b43527c \ No newline at end of file +b43527c +60176fa \ No newline at end of file diff --git a/OfficialWebsite/index.html b/OfficialWebsite/index.html index c8fcd59..70a3359 100644 --- a/OfficialWebsite/index.html +++ b/OfficialWebsite/index.html @@ -453,6 +453,9 @@

Minecraft 服务器智能守护进程

TCP 检测 · 自动重启 · 防循环冷却

SCU 团队 拥有 · 编写者 S.A. [@SNOWARE]

+

+ 当前版本 60176fa · 频道 master +

diff --git a/config.rb b/config.rb index e673524..d43bc20 100644 --- a/config.rb +++ b/config.rb @@ -15,7 +15,7 @@ # frozen_string_literal: true # @type const INITIAL_GRACE: Integer -INITIAL_GRACE = 300 # 初次启动等待 300 秒 +INITIAL_GRACE = 300 # 初次启动等待 300 秒 # @type const TRY_TIMES: Integer TRY_TIMES = 3 # 尝试次数 # @type const RST_COOLDOWN_DURATION: Integer diff --git a/main.rb b/main.rb index 5465e8b..75b384e 100644 --- a/main.rb +++ b/main.rb @@ -12,59 +12,66 @@ # MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. # See the Mulan PubL v2 for more details. +# frozen_string_literal: true + +# rubocop:disable Metrics/MethodLength, Metrics/AbcSize def main puts ' ___ __ ___ ___ ___ ' puts ' |\/| | |\ | |__ /__` |__ |\ | | | |\ | |__ | ' puts ' | | | | \| |___ .__/ |___ | \| | | | \| |___ |___ ' - puts " MineSentinel dev-rolling version by <*> S.A. [@SNOWARE] and owned by SCU team." - puts " Copyleft (c) 2026 S.A. SNOWARE-SCU. This program is licensed under Mulan PubL v2." - puts " E-mail: SALflake@qq.com | QQ: 758522192 | Website: https://swe-iss.rth1.xyz/softwares/minesentinel." + puts ' MineSentinel dev-rolling version by <*> S.A. [@SNOWARE] and owned by SCU team.' + puts ' Copyleft (c) 2026 S.A. SNOWARE-SCU. This program is licensed under Mulan PubL v2.' + puts ' E-mail: SALflake@qq.com | QQ: 758522192 | Website: https://swe-iss.rth1.xyz/softwares/minesentinel.' puts require_relative 'method_executor' # 日志系统在此时初始化 - MethodExecutor.record("debug", "init", "Initialized MethodExecutor, which implements logging system and intervening functions.") + MethodExecutor.record('debug', 'init', + 'Initialized MethodExecutor, which implements logging system and intervening functions.') require_relative 'server_status_detector' require_relative 'systemd_notifier' # 心跳系统在此时初始化 require_relative 'config' - MethodExecutor.record("debug", "init", "Initialized SystemdNotifier, which implements systemd watchdog feeder.") + MethodExecutor.record('debug', 'init', 'Initialized SystemdNotifier, which implements systemd watchdog feeder.') # 确认是否具有展开干预操作的权限 如果因场景不同不需要请注释这部分内容 - unless Process.euid == 0 - MethodExecutor.record("error", "init", "Permission denied: root required to intervene Systemd Services.") + unless Process.euid.zero? + MethodExecutor.record('error', 'init', 'Permission denied: root required to intervene Systemd Services.') raise Errno::EPERM end - MethodExecutor.record("info", "mainloop", "MineSentinel started, entering mainloop.") + MethodExecutor.record('info', 'mainloop', 'MineSentinel started, entering mainloop.') require_relative 'updates' begin upd_result = UpdateManager.check if upd_result[:status] == :NewerAvailable - MethodExecutor.record("info", "updates", "Update #{upd_result[:version]} available, please check the git repo for details.") + MethodExecutor.record('info', 'updates', + "Update #{upd_result[:version]} available, please check the git repo for details.") end - rescue => check_error - MethodExecutor.record("error", "updates", "#{check_error.to_s}") + rescue StandardError => e + MethodExecutor.record('error', 'updates', e.to_s) end # 初次启动:MineSentinel 先于 minecraft 启动(systemd Before=), # 等待服务器端口就绪,避免误判为崩溃 - MethodExecutor.record("info", "mainloop", - "Initial startup grace period #{INITIAL_GRACE}s — waiting for server to come up...") + MethodExecutor.record('info', 'mainloop', + "Initial startup grace period #{INITIAL_GRACE}s — waiting for server to come up...") grace_deadline = Time.now + INITIAL_GRACE while Time.now < grace_deadline sleep 5 - if ServerStatusDetector::MinecraftServer.self_diag[:type] - MethodExecutor.record("info", "mainloop", - "Server port is open during grace period, resuming normal monitoring.") - break - end + next unless ServerStatusDetector::MinecraftServer.self_diag[:type] + + MethodExecutor.record('info', 'mainloop', + 'Server port is open during grace period, resuming normal monitoring.') + break end - MethodExecutor.record("info", "mainloop", "Grace period ended.") + MethodExecutor.record('info', 'mainloop', 'Grace period ended.') main_loop end +# rubocop:enable Metrics/MethodLength, Metrics/AbcSize # Functional mainloop. +# rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity def main_once chkrslts = [] (0..TRY_TIMES).each do @@ -72,43 +79,46 @@ def main_once chkrslts.append(chkrslt) end if chkrslts.all? { |chkrslt_in| chkrslt_in[:type] } - MethodExecutor.record("debug", "mainloop", "Server health check passed.") + MethodExecutor.record('debug', 'mainloop', 'Server health check passed.') else chkrslts.each do |result| - MethodExecutor.record("warn", "mainloop", "Server health check FAILED: #{result[:message]}") + MethodExecutor.record('warn', 'mainloop', "Server health check FAILED: #{result[:message]}") end - MethodExecutor.record("info", "mainloop", "Checked #{TRY_TIMES} time.") - if chkrslts.any? { |chkrslt_in| chkrslt_in[:operation].include?("Record") } - MethodExecutor.record("warn", "mainloop", "Action: Record -> Server died!") - if chkrslts.any? { |chkrslt_in| chkrslt_in[:message] == "Server died for unknown reason, and will be restarted." } - MethodExecutor.record("info", "mainloop", "Action: ForceResetServerProcess triggered.") + MethodExecutor.record('info', 'mainloop', "Checked #{TRY_TIMES} time.") + if chkrslts.any? { |chkrslt_in| chkrslt_in[:operation].include?('Record') } + MethodExecutor.record('warn', 'mainloop', 'Action: Record -> Server died!') + if chkrslts.any? { |chkrslt_in| chkrslt_in[:message] == 'Server died for unknown reason, and will be restarted.' } + MethodExecutor.record('info', 'mainloop', 'Action: ForceResetServerProcess triggered.') MethodExecutor.reset_minecraft_server # 重启后等待服务器启动(MC服务器启动通常需要30秒~2分钟) cooldown_beg = Time.now - MethodExecutor.record("info", "mainloop", "Cooling down #{RST_COOLDOWN_DURATION}s for server startup...") + MethodExecutor.record('info', 'mainloop', "Cooling down #{RST_COOLDOWN_DURATION}s for server startup...") + # rubocop:disable Metrics/BlockNesting while Time.now < cooldown_beg + RST_COOLDOWN_DURATION sleep 1 - SystemdNotifier.notify("WATCHDOG=1") + SystemdNotifier.notify('WATCHDOG=1') end - MethodExecutor.record("info", "mainloop", "Cooldown finished, resuming monitoring.") + # rubocop:enable Metrics/BlockNesting + MethodExecutor.record('info', 'mainloop', 'Cooldown finished, resuming monitoring.') end end end end +# rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity def main_loop - while true + loop do sleep CHECK_INTERVAL - SystemdNotifier.notify("WATCHDOG=1") + SystemdNotifier.notify('WATCHDOG=1') main_once end end begin main -rescue => e +rescue StandardError => e # 强制输出到stderr - $stderr.puts "[CRITICAL] Unhandled exception: #{e.message}" - $stderr.puts e.backtrace&.join("\n") + warn "[CRITICAL] Unhandled exception: #{e.message}" + warn e.backtrace&.join("\n") exit 1 end diff --git a/method_executor.rb b/method_executor.rb index 827ec7f..733eab2 100644 --- a/method_executor.rb +++ b/method_executor.rb @@ -18,30 +18,31 @@ PERSISTENCE_PERIOD = 30 # Wait for systemctl to take the order. require 'logger' require_relative 'server_status_detector' + +# rubocop:disable Style/ClassVars class MethodExecutor - public RST_COUNTS_LIMIT = 10 @@rst_counter = 0 # Initialize the standard log system. - @@logger = Logger.new(STDOUT) # This script will be launched as a systemd server. - STDOUT.sync = true - STDERR.sync = true + @@logger = Logger.new($stdout) # This script will be launched as a systemd server. + $stdout.sync = true + $stderr.sync = true # Fetch the environmental variable and convert it into log level. - @@logger.level = case ENV["LOG_LEVEL"] # @为单个实例(半生不熟的概念,貌似是在不同的空间独立创建),@@为共享同一个 - when nil # Default config. - Logger::Severity::DEBUG - when "debug" - Logger::Severity::DEBUG - when "info" - Logger::Severity::INFO - when "warn" - Logger::Severity::WARN - when "error" - Logger::Severity::ERROR - else - raise ArgumentError + @@logger.level = case ENV.fetch('LOG_LEVEL', nil) # @为单个实例(半生不熟的概念,貌似是在不同的空间独立创建),@@为共享同一个 + when nil, 'debug' # Default config. + Logger::Severity::DEBUG + when 'info' + Logger::Severity::INFO + when 'warn' + Logger::Severity::WARN + when 'error' + Logger::Severity::ERROR + else + raise ArgumentError end - @@logger.progname = "MineSentinel" + @@logger.progname = 'MineSentinel' + + # rubocop:disable Metrics/MethodLength # @param level [String] "info" | "warn" | "error" | "debug" # @param module_name [String] @@ -49,82 +50,85 @@ class MethodExecutor # @return [void] def self.record(level, module_name, message) case level - when "info" + when 'info' @@logger.info("#{module_name}: #{message}") - when "warn" + when 'warn' @@logger.warn("#{module_name}: #{message}") - when "error" + when 'error' @@logger.error("#{module_name}: #{message}") - when "debug" + when 'debug' @@logger.debug("#{module_name}: #{message}") else raise ArgumentError end end + # rubocop:enable Metrics/MethodLength + # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity + # @return [void] def self.reset_minecraft_server - - self.record("info", "reset", "Attempt ##{@@rst_counter + 1} to reset Minecraft server.") + record('info', 'reset', "Attempt ##{@@rst_counter + 1} to reset Minecraft server.") if @@rst_counter >= RST_COUNTS_LIMIT - self.record("error", "reset", - "Tried #{RST_COUNTS_LIMIT} times and failed, now I'll idle until server is manually restarted.") - while true + record('error', 'reset', + "Tried #{RST_COUNTS_LIMIT} times and failed, now I'll idle until server is manually restarted.") + loop do sleep 10 - SystemdNotifier.notify("WATCHDOG=1") if defined?(SystemdNotifier) - if ServerStatusDetector::MinecraftServer.self_diag[:type] == true - self.record("info", "reset", "Server recovered! Resuming normal monitoring.") - @@rst_counter = 0 - return - end + SystemdNotifier.notify('WATCHDOG=1') if defined?(SystemdNotifier) + next unless ServerStatusDetector::MinecraftServer.self_diag[:type] == true + + record('info', 'reset', 'Server recovered! Resuming normal monitoring.') + @@rst_counter = 0 + return end end @@rst_counter += 1 - unless Process.euid == 0 - self.record("error", "reset", "Permission denied: root required to manage systemd services.") + unless Process.euid.zero? + record('error', 'reset', 'Permission denied: root required to manage systemd services.') raise Errno::EPERM end - self.record("info", "reset", "Spawning 'systemctl restart minecraft' (PID will be captured)...") - pid = spawn "systemctl restart minecraft" - self.record("debug", "reset", "systemctl restart spawned with PID #{pid}.") + record('info', 'reset', "Spawning 'systemctl restart minecraft' (PID will be captured)...") + pid = spawn 'systemctl restart minecraft' + record('debug', 'reset', "systemctl restart spawned with PID #{pid}.") tick_beg = Time.now - while true + # rubocop:disable Metrics/BlockLength + loop do sleep 0.5 - SystemdNotifier.notify("WATCHDOG=1") + SystemdNotifier.notify('WATCHDOG=1') proc_status = Process.waitpid2(pid, Process::WNOHANG) if proc_status # 子进程已退出,返回 [pid, status] _, status = proc_status elapsed = (Time.now - tick_beg).round(2) if status.success? - self.record("info", "reset", "'systemctl restart' succeeded in #{elapsed}s.") + record('info', 'reset', "'systemctl restart' succeeded in #{elapsed}s.") @@rst_counter = 0 # 成功时重置计数器 - break else - self.record("error", "reset", "'systemctl restart' failed with exitcode #{status.exitstatus} after #{elapsed}s.") - break + record('error', 'reset', + "'systemctl restart' failed with exitcode #{status.exitstatus} after #{elapsed}s.") end + break else # 子进程仍在运行 (WNOHANG 返回 nil) - if Time.now - tick_beg > PERSISTENCE_PERIOD then - self.record("warn", "reset", - "'systemctl restart' timed out after #{PERSISTENCE_PERIOD}s. Force-killing PID #{pid}...") + if Time.now - tick_beg > PERSISTENCE_PERIOD + record('warn', 'reset', + "'systemctl restart' timed out after #{PERSISTENCE_PERIOD}s. Force-killing PID #{pid}...") system "kill -9 #{pid}" - self.record("warn", "reset", "Running 'systemctl kill minecraft' (last resort)...") - system "systemctl kill minecraft" # 没招了,毁灭吧 + record('warn', 'reset', "Running 'systemctl kill minecraft' (last resort)...") + system 'systemctl kill minecraft' # 没招了,毁灭吧 sleep 0.5 - self.record("info", "reset", "Running 'systemctl start minecraft' (rebirth)...") - system "systemctl start minecraft" # 重生 - self.record("info", "reset", "Force-reset sequence completed.") + record('info', 'reset', "Running 'systemctl start minecraft' (rebirth)...") + system 'systemctl start minecraft' # 重生 + record('info', 'reset', 'Force-reset sequence completed.') break end end + # rubocop:enable Metrics/BlockLength end - - end - + # rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity + # rubocop:enable Style/ClassVars end diff --git a/server_status_detector.rb b/server_status_detector.rb index 51f3230..743e8fb 100644 --- a/server_status_detector.rb +++ b/server_status_detector.rb @@ -19,12 +19,12 @@ module ServerStatusDetector require 'timeout' class TcpServer - + # rubocop:disable Metrics/MethodLength, Metrics/AbcSize # @param host [String] # @param port [Integer] # @param timeout [Integer] # @return [Hash{Symbol=>Integer, Symbol=>Symbol, Symbol=>Float,nil}] - def self.test_port(host = '127.0.0.1', port = 25565, timeout = 10) + def self.test_port(host = '127.0.0.1', port = 25_565, timeout = 10) result = { port: port, status: :unknown, @@ -34,12 +34,11 @@ module ServerStatusDetector # 带错误处理的代码需要使用 begin...end begin Timeout.timeout(timeout) do # 此方法接收整数和代码块作为参数,并在超时时引发错误 - socket = TCPSocket.new(host,port) + socket = TCPSocket.new(host, port) result[:status] = :open result[:response_time] = (Time.now - begin_time).round(4) socket.close end - rescue Timeout::Error result[:status] = :timeout rescue Errno::ECONNREFUSED @@ -48,66 +47,68 @@ module ServerStatusDetector result[:status] = :interrupted rescue Errno::ENETUNREACH, Errno::EHOSTUNREACH result[:status] = :unreachable - rescue => error + rescue StandardError => e result[:status] = :error - result[:error] = error.message + result[:error] = e.message end result end + # rubocop:enable Metrics/MethodLength, Metrics/AbcSize end - class MinecraftServer < TcpServer # 继承为TcpServer编写的属性和方法 - + # 继承为TcpServer编写的属性和方法 + class MinecraftServer < TcpServer + # rubocop:disable Metrics/MethodLength # @return [Hash{Symbol=>Boolean, Symbol=>Array, Symbol=>String}] def self_diag + res = test_port('127.0.0.1', 25_565) - res = test_port('127.0.0.1', 25565) - - if res[:error] != nil + unless res[:error].nil? message = "Unexpected ERROR when testing SMP by TCP: #{res[:status]},INFO: #{res[:error]}" puts message return { type: false, operation: %w[Record], - message: message, + message: message } - end # 立刻错误处理是好习惯 + # 立刻错误处理是好习惯 + end case res[:status] when :open - MethodExecutor.record("debug", "self_diag", + MethodExecutor.record('debug', 'self_diag', "TCP check OK — #{res[:response_time]}s") - return { - type: true, + { + type: true } when :timeout - MethodExecutor.record("warn", "self_diag", - "TCP check timed out.") - return { + MethodExecutor.record('warn', 'self_diag', + 'TCP check timed out.') + { type: false, operation: %w[Record], - message: "We sent packet without response." + message: 'We sent packet without response.' } when :closed - MethodExecutor.record("warn", "self_diag", - "TCP port closed — server is down.") - return { + MethodExecutor.record('warn', 'self_diag', + 'TCP port closed — server is down.') + { type: false, operation: %w[Record ForceResetServerProcess], # 重启时注意要设置最大尝试次数 - message: "Server died for unknown reason, and will be restarted." + message: 'Server died for unknown reason, and will be restarted.' } else - MethodExecutor.record("warn", "self_diag", + MethodExecutor.record('warn', 'self_diag', "TCP check returned unexpected status: #{res[:status]}.") - return { + { type: false, operation: %w[Record], message: "Unexpected status: #{res[:status]}." } end - end + # rubocop:enable Metrics/MethodLength end end diff --git a/systemd_notifier.rb b/systemd_notifier.rb index 40cd917..c027e68 100644 --- a/systemd_notifier.rb +++ b/systemd_notifier.rb @@ -14,25 +14,27 @@ # frozen_string_literal: true +# Sends keep-alive notifications (including WATCHDOG=1) to systemd +# via the NOTIFY_SOCKET datagram socket, enabling service supervision. class SystemdNotifier require_relative 'method_executor' - public - @notify_socket = ENV["NOTIFY_SOCKET"] - @watchdog_usec = (ENV["WATCHDOG_USEC"] || '0').to_i - unless @notify_socket && File.socket?(@notify_socket) - MethodExecutor.record("error", "heartbeat", "NOTIFY_SOCKET is not set or is not a socket, am I a service?") - @enable_heartbeat = false - else - MethodExecutor.record("debug", "heartbeat", "Found NOTIFY_SOCKET, enabling heartbeat") + @notify_socket = ENV.fetch('NOTIFY_SOCKET', nil) + @watchdog_usec = (ENV['WATCHDOG_USEC'] || '0').to_i + + if @notify_socket && File.socket?(@notify_socket) + MethodExecutor.record('debug', 'heartbeat', 'Found NOTIFY_SOCKET, enabling heartbeat') @enable_heartbeat = true # Fix(2026-06-08): 将 UNIXSocket(SOCK_STREAM) 改为 SOCK_DGRAM # systemd NOTIFY_SOCKET 是 datagram socket,用 stream socket 连接会报 # Protocol wrong type for socket (Errno::EPROTOTYPE) @socket = Socket.new(Socket::AF_UNIX, Socket::SOCK_DGRAM) @socket.connect(Socket.pack_sockaddr_un(@notify_socket)) - MethodExecutor.record("debug", "heartbeat", "Heartbeat socket opened") + MethodExecutor.record('debug', 'heartbeat', 'Heartbeat socket opened') + else + MethodExecutor.record('error', 'heartbeat', 'NOTIFY_SOCKET is not set or is not a socket, am I a service?') + @enable_heartbeat = false end # @return [Boolean] @@ -44,13 +46,13 @@ class SystemdNotifier # @return [void] def self.notify(message) return unless @enable_heartbeat + begin @socket.send(message, 0) - MethodExecutor.record("debug", "heartbeat", "Heartbeat sent: #{message}") - rescue => e - MethodExecutor.record("error", "heartbeat", "Failed to send heartbeat: #{e}") + MethodExecutor.record('debug', 'heartbeat', "Heartbeat sent: #{message}") + rescue StandardError => e + MethodExecutor.record('error', 'heartbeat', "Failed to send heartbeat: #{e}") @enable_heartbeat = false end end - end diff --git a/updates.rb b/updates.rb index ea53de1..43d407c 100644 --- a/updates.rb +++ b/updates.rb @@ -18,7 +18,7 @@ require 'net/http' require 'uri' # @type const RELEASE_SERIAL_CODE: String -RELEASE_SERIAL_CODE = 'b43527c' +RELEASE_SERIAL_CODE = '60176fa' # @type const CHANNEL: String CHANNEL = 'master' # @type const VERSION_LIST_URL: String @@ -27,42 +27,46 @@ VERSION_LIST_URL = "https://raw.giteeusercontent.com/scu_team/minesentinel/#{CHA RECURSIVE_RETRY_DEPTH = 3 class UpdateManager + # rubocop:disable Metrics/MethodLength # @return [String] def self.fetch - MethodExecutor.record("debug", "updates", "Fetching version list from #{VERSION_LIST_URL}") + MethodExecutor.record('debug', 'updates', "Fetching version list from #{VERSION_LIST_URL}") uri = URI(VERSION_LIST_URL) response = Net::HTTP.get_response(uri) case response.code.to_i when 200 - MethodExecutor.record("debug", "updates", "Version list fetched successfully.") + MethodExecutor.record('debug', 'updates', 'Version list fetched successfully.') response.body else raise "Unexpected HTTP response code: #{response.code}" end - rescue => e - MethodExecutor.record("error", "updates", "Failed to fetch version list: #{e}") + rescue StandardError => e + MethodExecutor.record('error', 'updates', "Failed to fetch version list: #{e}") nil end + # rubocop:enable Metrics/MethodLength + # rubocop:disable Metrics/MethodLength # @return [Hash{Symbol=>Symbol, Symbol=>String}] def self.check raw = fetch if raw.nil? - MethodExecutor.record("error", "updates", "Version list is empty, cannot check updates.") - raise "Unknown Error" + MethodExecutor.record('error', 'updates', 'Version list is empty, cannot check updates.') + raise 'Unknown Error' end lines = raw.lines(chomp: true) lines.each_with_index do |line, index| line_num = index + 1 if line == RELEASE_SERIAL_CODE && line_num == lines.size - MethodExecutor.record("info", "updates", "Current version #{RELEASE_SERIAL_CODE} is up to date.") + MethodExecutor.record('info', 'updates', "Current version #{RELEASE_SERIAL_CODE} is up to date.") return { type: :Newest, version: RELEASE_SERIAL_CODE } end end latest = lines.last - MethodExecutor.record("info", "updates", "Newer version #{latest} available (current: #{RELEASE_SERIAL_CODE}).") + MethodExecutor.record('info', 'updates', "Newer version #{latest} available (current: #{RELEASE_SERIAL_CODE}).") { type: :NewerAvailable, version: latest } end + # rubocop:enable Metrics/MethodLength end