Files
hatchet/examples/ruby/worker_fixture.rb
Gabe Ruttner 7875d78057 Feat: Official Ruby SDK (#3004)
* feat: initial ruby sdk

* fix: run listener

* fix: scope

* feat: rest feature clients

* fix: bugs

* fix: concurrent register

* fix: tests and ergonomics

* docs: all of them

* chore: lint

* feat: add RBS

* feat: add GitHub Actions workflow for Ruby SDK with linting, testing, and publishing steps

* chore: lint

* refactor: simplify load path setup for Hatchet REST client and remove symlink creation

* fix: cert path

* fix: test

* fix: blocking

* fix: ensure Hatchet client is only initialized once across examples

* fix: tests

* remove: unused example

* fix: bubble up errors

* test: skip flaky for now

* remove: lifespans

* fix: durable context bugs

* fix: bulk replay

* fix: tests

* cleanup: generate tooling

* fix: integration test

* chore: lint

* release: 0.1.0

* chore: remove python comments

* refactor: remove OpenTelemetry configuration and related unused options

* fix: default no healthcheck

* chore: lockfile

* feat: register as ruby

* chore: lint

* chore: update py/ts apis to include ruby

* chore: docs pass

* chore: lint

* chore: generate

* chore: cleanup

* chore: generate examples

* tests: add e2e tests

* tests: cache examples dependencies

* fix: namespace

* fix: namespace

* fix: namespaces

* chore:lint

* fix: improve cancellation workflow polling logic and add error handling

* revert: py/ts versions
2026-02-15 14:32:15 -08:00

98 lines
2.4 KiB
Ruby

# frozen_string_literal: true
require "open3"
require "net/http"
require "logger"
require "timeout"
module HatchetWorkerFixture
LOGGER = Logger.new($stdout)
# Wait for the worker health check endpoint to respond
#
# @param port [Integer] Health check port
# @param max_attempts [Integer] Maximum number of attempts
# @return [Boolean] true if healthy
# @raise [RuntimeError] if worker fails to start
def self.wait_for_worker_health(port:, max_attempts: 25)
attempts = 0
loop do
if attempts > max_attempts
raise "Worker failed to start within #{max_attempts} seconds"
end
begin
uri = URI("http://localhost:#{port}/health")
response = Net::HTTP.get_response(uri)
return true if response.code == "200"
rescue StandardError
# Worker not ready yet
end
sleep 1
attempts += 1
end
end
# Start a worker subprocess and wait for it to be healthy
#
# @param command [Array<String>] Command to run
# @param healthcheck_port [Integer] Port for health checks
# @yield [pid] Yields the process PID
# @return [void]
def self.with_worker(command, healthcheck_port: 8001)
LOGGER.info("Starting background worker: #{command.join(' ')}")
ENV["HATCHET_CLIENT_WORKER_HEALTHCHECK_PORT"] = healthcheck_port.to_s
stdin, stdout, stderr, wait_thr = Open3.popen3(*command)
pid = wait_thr.pid
# Log output in background threads
Thread.new do
stdout.each_line { |line| puts line.chomp }
rescue IOError
# Stream closed
end
Thread.new do
stderr.each_line { |line| $stderr.puts line.chomp }
rescue IOError
# Stream closed
end
wait_for_worker_health(port: healthcheck_port)
yield pid
ensure
LOGGER.info("Cleaning up background worker (PID: #{pid})")
if pid
begin
# Kill process group to get children too
Process.kill("TERM", -Process.getpgid(pid))
rescue Errno::ESRCH, Errno::EPERM
# Process already gone
end
begin
Timeout.timeout(5) { Process.wait(pid) }
rescue Timeout::Error
begin
Process.kill("KILL", pid)
Process.wait(pid)
rescue Errno::ESRCH, Errno::ECHILD
# Already gone
end
rescue Errno::ECHILD
# Already reaped
end
end
[stdin, stdout, stderr].each do |io|
io&.close rescue nil
end
end
end