diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 87672bc..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,63 +0,0 @@ -# Inspired by: http://mikebian.co/running-tests-against-multiple-ruby-versions-using-circleci/ - -version: 2.1 - -orbs: - ruby: circleci/ruby@1.1 - - - # If you want to work on `action_subscriber` you will need to have a rabbitmq instance running locally on port 5672 with a management plugin enabled on port 15672. Usually the easiest way to accomplish this is to use docker and run the command: - - # ``` - -jobs: - test: - parallelism: 1 - parameters: - ruby-image: - type: string - docker: - - image: << parameters.ruby-image >> - - image: rabbitmq:3.11-management - - steps: - - checkout - - run: - name: install dockerize - command: wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz && sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz && rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz - environment: - DOCKERIZE_VERSION: v0.3.0 - # - run: - # name: Wait for rabbitmq - # command: dockerize -wait tcp://localhost:5672 -timeout 1m - - run: - name: Install bundler - command: gem install bundler - - run: - name: Which bundler? - command: bundle -v - - run: - name: bundle install - command: bundle install - - run: - name: rspec - command: bundle exec rspec - -# strangely, there seems to be very little documentation about exactly how martix builds work. -# By defining a param inside your job definition, Circle CI will automatically spawn a job for -# unique param value passed via `matrix`. Neat! -# https://circleci.com/blog/circleci-matrix-jobs/ -workflows: - build_and_test: - jobs: - - test: - matrix: - parameters: - ruby-image: - - circleci/ruby:2.6 - - circleci/ruby:2.7 - - cimg/ruby:3.0 - - cimg/ruby:3.1 - - circleci/jruby:9.2 - - circleci/jruby:9.3 - # - circleci/jruby:9.4 (TODO: Restore once this image exists) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..1ce276c --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,96 @@ +name: Ruby Compatibility Matrix + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build_and_test: + name: test-${{ matrix.docker_image }} + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + docker_image: + - "ruby:3.1" + - "ruby:3.4" + - "jruby:9.4" + - "jruby:10.0" + + container: + image: ${{ matrix.docker_image }} + env: + JRUBY_OPTS: "-J-Xmx1024m" + RAILS_ENV: test + # Use the service name 'rabbitmq' as the host, not localhost + RABBITMQ_URL: "amqp://demo:demo@rabbitmq:5672" + RABBITMQ_HOST: rabbitmq + RABBITMQ_MANAGEMENT_URL: "http://demo:demo@rabbitmq:15672" + + services: + rabbitmq: + image: rabbitmq:3.12-management + env: + RABBITMQ_DEFAULT_USER: demo + RABBITMQ_DEFAULT_PASS: demo + # Note: Health checks here ensure the container is up before steps start + options: >- + --health-cmd "rabbitmq-diagnostics -q check_running" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install System Dependencies + run: | + if [ "$(id -u)" = "0" ]; then + apt-get update && apt-get install -y build-essential git netcat-openbsd docker-ce-cli + else + sudo apt-get update && sudo apt-get install -y build-essential git netcat-openbsd docker-ce-cli + fi + + - name: Cache Ruby Gems + uses: actions/cache@v4 + with: + path: vendor/bundle + key: v1-gems-${{ matrix.docker_image }}-${{ hashFiles('Gemfile.lock') }} + restore-keys: | + v1-gems-${{ matrix.docker_image }}- + + - name: Install Ruby Dependencies + run: | + gem install bundler + bundle config set --local path 'vendor/bundle' + bundle install --jobs=4 --retry=3 + + - name: Wait for RabbitMQ + run: | + echo "Waiting for RabbitMQ to start at hostname 'rabbitmq'..." + for i in {1..30}; do + if nc -z rabbitmq 5672; then + echo "RabbitMQ is ready!" + exit 0 + fi + echo "Waiting... ($i/30)" + sleep 1 + done + echo "RabbitMQ failed to start" + exit 1 + + - name: Configure RabbitMQ Management user "demo" + run: | + docker exec ${{ job.services.rabbitmq.id }} rabbitmqctl set_user_tags "demo" administrator + docker exec ${{ job.services.rabbitmq.id }} rabbitmqctl set_permissions -p / "demo" ".*" ".*" ".*" + + - name: Run Tests + run : | + git config --global --add safe.directory /__w/action_subscriber/action_subscriber + cp ./config/action_subscriber.yml.sample ./config/action_subscriber.yml + echo $(env) + bundle exec rspec diff --git a/.gitignore b/.gitignore index 88db647..d7c7dc3 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,6 @@ Gemfile.lock .yardoc _yardoc doc/ + +config/active_publisher.yml +config/action_subscriber.yml diff --git a/action_subscriber.gemspec b/action_subscriber.gemspec index 1e462f4..368a097 100644 --- a/action_subscriber.gemspec +++ b/action_subscriber.gemspec @@ -18,24 +18,26 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency "activesupport", ">= 3.2" + spec.add_dependency "activesupport", ">= 6.0" if ENV["PLATFORM"] == "java" || ::RUBY_PLATFORM == "java" spec.platform = "java" spec.add_dependency "march_hare", "~> 4.4" else - spec.add_dependency "bunny", ">= 1.5.0" + spec.add_dependency "bunny", ">= 2.24.0" end spec.add_dependency "concurrent-ruby" spec.add_dependency "middleware" spec.add_dependency "thor" - spec.add_development_dependency "active_publisher", "~> 0.1.5" - spec.add_development_dependency "activerecord", ">= 3.2" - spec.add_development_dependency "bundler", ">= 1.6" + spec.required_ruby_version = '>= 3.1.0' + + spec.add_development_dependency "active_publisher", "1.6.0.pre1" + spec.add_development_dependency "activerecord", ">= 6.0" + spec.add_development_dependency "bundler" spec.add_development_dependency "pry-nav" spec.add_development_dependency "rabbitmq_http_api_client", "~> 1.15.0" - spec.add_development_dependency "rspec", "~> 3.0" + spec.add_development_dependency "rspec" spec.add_development_dependency "rake" spec.add_development_dependency "simplecov" end diff --git a/config/action_subscriber.yml.sample b/config/action_subscriber.yml.sample new file mode 100644 index 0000000..39caa8a --- /dev/null +++ b/config/action_subscriber.yml.sample @@ -0,0 +1,12 @@ +--- +defaults: &defaults + heartbeat: 15 + host: rabbitmq + network_recovery_interval: 5 + port: 5672 + username: demo + password: demo + +production: *defaults +development: *defaults +test: *defaults diff --git a/lib/action_subscriber/configuration.rb b/lib/action_subscriber/configuration.rb index 5e67adc..060e6b6 100644 --- a/lib/action_subscriber/configuration.rb +++ b/lib/action_subscriber/configuration.rb @@ -40,7 +40,7 @@ class Configuration :host => 'localhost', :hosts => [], :network_recovery_interval => NETWORK_RECOVERY_INTERVAL, - :password => "guest", + :password => 'guest', :port => 5672, :prefetch => 2, :resubscribe_on_consumer_cancellation => true, @@ -51,7 +51,7 @@ class Configuration :tls_ca_certificates => [], :tls_cert => nil, :tls_key => nil, - :username => "guest", + :username => 'guest', :verify_peer => true, :virtual_host => "/" } @@ -67,11 +67,15 @@ def self.configure_from_yaml_and_cli(cli_options = {}, reload = false) yaml_config = {} absolute_config_path = ::File.expand_path(::File.join("config", "action_subscriber.yml")) - if ::File.exists?(absolute_config_path) + puts "Trying to look for config file at #{absolute_config_path}" + if ::File.exist?(absolute_config_path) + puts "config file exists, loading" erb_yaml = ::ERB.new(::File.read(absolute_config_path)).result # Defined in Psych 3.2+ and the new canonical way to load trusted documents: # https://github.com/ruby/psych/issues/533#issuecomment-1019363688 yaml_config = ::YAML.respond_to?(:unsafe_load) ? ::YAML.unsafe_load(erb_yaml)[env] : ::YAML.load(erb_yaml)[env] + else + puts "config file does not exist" end ::ActionSubscriber::Configuration::DEFAULTS.each_pair do |key, value| diff --git a/lib/action_subscriber/middleware/active_record/connection_management.rb b/lib/action_subscriber/middleware/active_record/connection_management.rb index 5b424e5..c4d2ab4 100644 --- a/lib/action_subscriber/middleware/active_record/connection_management.rb +++ b/lib/action_subscriber/middleware/active_record/connection_management.rb @@ -16,7 +16,7 @@ def self.start_timed_task! :execution_interval => ::ActionSubscriber.config.connection_reaping_interval, :timeout_interval => ::ActionSubscriber.config.connection_reaping_timeout_interval) do - ::ActiveRecord::Base.clear_active_connections! + ::ActiveRecord::Base.connection_handler.clear_active_connections! end timed_task.execute diff --git a/lib/action_subscriber/rabbit_connection.rb b/lib/action_subscriber/rabbit_connection.rb index f6ca0e3..0977391 100644 --- a/lib/action_subscriber/rabbit_connection.rb +++ b/lib/action_subscriber/rabbit_connection.rb @@ -29,7 +29,11 @@ def self.create_connection options[:executor_factory] = ::Proc.new do ::MarchHare::ThreadPools.fixed_of_size(options[:threadpool_size]) end - connection = ::MarchHare.connect(options) + + puts "march_hare new connection = #{options}" + + + connection = ::MarchHare.connect(**options) connection.on_blocked do |reason| on_blocked(reason) end @@ -39,6 +43,9 @@ def self.create_connection connection else connection = ::Bunny.new(options) + + puts "bunny new connection = #{options}" + connection.start connection.on_blocked do |blocked_message| on_blocked(blocked_message.reason) diff --git a/lib/action_subscriber/version.rb b/lib/action_subscriber/version.rb index a0a9d73..cdfa208 100644 --- a/lib/action_subscriber/version.rb +++ b/lib/action_subscriber/version.rb @@ -1,3 +1,3 @@ module ActionSubscriber - VERSION = "5.3.3" + VERSION = "5.4.0" end diff --git a/spec/integration/automatic_reconnect_spec.rb b/spec/integration/automatic_reconnect_spec.rb index f19cb9b..d68c8ae 100644 --- a/spec/integration/automatic_reconnect_spec.rb +++ b/spec/integration/automatic_reconnect_spec.rb @@ -12,7 +12,10 @@ def spoke default_routes_for GusSubscriber end end - let(:http_client) { RabbitMQ::HTTP::Client.new("http://127.0.0.1:15672") } + let(:rabbitmq_management_url) { + ENV["RABBITMQ_MANAGEMENT_URL"] || "http://127.0.0.1:15672" + } + let(:http_client) { RabbitMQ::HTTP::Client.new(rabbitmq_management_url) } let(:subscriber) { GusSubscriber } it "reconnects when a connection drops" do diff --git a/spec/integration/consumer_cancellation_spec.rb b/spec/integration/consumer_cancellation_spec.rb index eb25aa3..dec12ee 100644 --- a/spec/integration/consumer_cancellation_spec.rb +++ b/spec/integration/consumer_cancellation_spec.rb @@ -13,7 +13,11 @@ def created default_routes_for ::YoloSubscriber end end - let(:http_client) { ::RabbitMQ::HTTP::Client.new("http://127.0.0.1:15672") } + + let(:rabbitmq_management_url) { + ENV["RABBITMQ_MANAGEMENT_URL"] || "http://127.0.0.1:15672" + } + let(:http_client) { RabbitMQ::HTTP::Client.new(rabbitmq_management_url) } let(:subscriber) { ::YoloSubscriber } it "resubscribes on cancellation" do diff --git a/spec/lib/action_subscriber/middleware/active_record/connection_management_spec.rb b/spec/lib/action_subscriber/middleware/active_record/connection_management_spec.rb index 7053c02..5a1247f 100644 --- a/spec/lib/action_subscriber/middleware/active_record/connection_management_spec.rb +++ b/spec/lib/action_subscriber/middleware/active_record/connection_management_spec.rb @@ -6,7 +6,7 @@ before { pool = double("pool") allow(pool).to receive(:with_connection).and_yield - allow(ActiveRecord::Base).to receive(:clear_active_connections!) + allow(ActiveRecord::Base.connection_handler).to receive(:clear_active_connections!) allow(ActiveRecord::Base).to receive(:connection_pool).and_return(pool) } diff --git a/spec/lib/action_subscriber/middleware/runner_spec.rb b/spec/lib/action_subscriber/middleware/runner_spec.rb index 3e85930..d546fd6 100644 --- a/spec/lib/action_subscriber/middleware/runner_spec.rb +++ b/spec/lib/action_subscriber/middleware/runner_spec.rb @@ -1,4 +1,5 @@ describe ActionSubscriber::Middleware::Runner do # TODO: Figure out at way to test this... - it "adds the router to the top of the stack" + it "adds the router to the top of the stack" do + end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index cd6b9a1..26aedb9 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -25,6 +25,10 @@ ::ActionSubscriber::Logging.initialize_logger(nil) ::ActionSubscriber.setup_default_threadpool! +# load from action_subscriber.yml for both ActivePublisher and ActionSubscriber +::ActivePublisher::Configuration.configure_from_yaml_and_cli +::ActionSubscriber::Configuration.configure_from_yaml_and_cli + RSpec.configure do |config| config.mock_with :rspec do |mocks| mocks.verify_partial_doubles = true diff --git a/spec/support/sample_config.yml b/spec/support/sample_config.yml index b156fc4..23c7b14 100644 --- a/spec/support/sample_config.yml +++ b/spec/support/sample_config.yml @@ -1,8 +1,12 @@ -development: - password: <%= "WAT" %> +--- +defaults: &defaults + heartbeat: 15 + host: localhost + network_recovery_interval: 5 + port: 5672 + username: WAT + password: WAT -test: - password: <%= "WAT" %> - -production: - password: <%= "WAT" %> +production: *defaults +development: *defaults +test: *defaults