diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3d21e370..f166385c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,6 +27,7 @@ jobs: - "4.0" - "3.4" - "3.3" + - "jruby" rack: - "~> 2.0" - "~> 3.0" @@ -34,12 +35,6 @@ jobs: - ruby: "4.0" rack: "~> 2.0" coverage: "true" - - ruby: "jruby" - rack: "~> 2.0" - optional: true - - ruby: "jruby" - rack: "~> 3.0" - optional: true env: COVERAGE: ${{ matrix.coverage }} RACK_MATRIX_VALUE: ${{ matrix.rack || '' }} diff --git a/Gemfile b/Gemfile index 7d05977d..2143d36f 100644 --- a/Gemfile +++ b/Gemfile @@ -49,7 +49,6 @@ gem "hanami-devtools", github: "hanami/devtools", branch: "main" group :test do gem "pry" - gem "readline" gem "rspec", "~> 3.9" gem "ostruct", require: false end diff --git a/lib/hanami/cli/commands/app/db/utils/database.rb b/lib/hanami/cli/commands/app/db/utils/database.rb index 7360a72a..bd4ed336 100644 --- a/lib/hanami/cli/commands/app/db/utils/database.rb +++ b/lib/hanami/cli/commands/app/db/utils/database.rb @@ -38,6 +38,10 @@ def sql require_relative("postgres") Postgres }, + "mysql" => -> { + require_relative("mysql") + Mysql + }, "mysql2" => -> { require_relative("mysql") Mysql @@ -45,10 +49,7 @@ def sql ).freeze def self.database_class(database_url) - database_scheme = URI(database_url).scheme - if database_scheme == "jdbc" - database_scheme = URI(database_url.sub("jdbc:", "")).scheme - end + database_scheme = URI(database_url.delete_prefix("jdbc:")).scheme DATABASE_CLASS_RESOLVER[database_scheme].call end @@ -86,8 +87,20 @@ def database_url slice.container.providers[:db].source.database_urls.fetch(gateway_name) end + # JDBC URLs (required to connect via JRuby) nest the real URL after "jdbc:", e.g. + # "jdbc:postgresql://localhost/app", so parse that nested URL instead. def database_uri - @database_uri ||= URI(database_url) + @database_uri ||= URI(database_url.delete_prefix("jdbc:")) + end + + # JDBC drivers expect the user and password as query params, e.g. + # "jdbc:postgresql://localhost/app?user=postgres&password=secret". + def database_user + database_uri.user || database_query_params["user"] + end + + def database_password + database_uri.password || database_query_params["password"] end def gateway @@ -197,10 +210,8 @@ def schema_migrations_sql_dump private - def database_path - database_uri.path || - # For `jdbc:` URIs the path is exposed via the opaque component - database_uri.opaque.sub(%r{^\w+:/?}, "") + def database_query_params + @database_query_params ||= URI.decode_www_form(database_uri.query.to_s).to_h end def jruby? diff --git a/lib/hanami/cli/commands/app/db/utils/mysql.rb b/lib/hanami/cli/commands/app/db/utils/mysql.rb index f14d943e..b15ddac2 100644 --- a/lib/hanami/cli/commands/app/db/utils/mysql.rb +++ b/lib/hanami/cli/commands/app/db/utils/mysql.rb @@ -71,13 +71,13 @@ def cli_options [].tap { |opts| opts << "--host=#{Shellwords.escape(database_uri.host)}" if database_uri.host opts << "--port=#{Shellwords.escape(database_uri.port)}" if database_uri.port - opts << "--user=#{Shellwords.escape(database_uri.user)}" if database_uri.user + opts << "--user=#{Shellwords.escape(database_user)}" if database_user }.join(" ") end def cli_env_vars @cli_env_vars ||= {}.tap do |vars| - vars["MYSQL_PWD"] = database_uri.password.to_s if database_uri.password + vars["MYSQL_PWD"] = database_password.to_s if database_password end end end diff --git a/lib/hanami/cli/commands/app/db/utils/postgres.rb b/lib/hanami/cli/commands/app/db/utils/postgres.rb index f65a2068..9e18ecd1 100644 --- a/lib/hanami/cli/commands/app/db/utils/postgres.rb +++ b/lib/hanami/cli/commands/app/db/utils/postgres.rb @@ -87,10 +87,12 @@ def escaped_name end def cli_env_vars - @cli_env_vars ||= %i[host port user password].each_with_object({}) do |field, vars| - value = database_uri.public_send(field).to_s - vars["PG#{field}".upcase] = value unless value.empty? - end + @cli_env_vars ||= { + "PGHOST" => database_uri.host, + "PGPORT" => database_uri.port, + "PGUSER" => database_user, + "PGPASSWORD" => database_password + }.transform_values(&:to_s).reject { |_, value| value.empty? } end end end diff --git a/lib/hanami/cli/commands/app/db/utils/sqlite.rb b/lib/hanami/cli/commands/app/db/utils/sqlite.rb index 74e01f65..459828fc 100644 --- a/lib/hanami/cli/commands/app/db/utils/sqlite.rb +++ b/lib/hanami/cli/commands/app/db/utils/sqlite.rb @@ -72,7 +72,7 @@ def name @name ||= begin raw = - if database_uri.scheme == "jdbc" + if database_url.start_with?("jdbc:") # For JDBC SQLite URIs like "jdbc:sqlite:db/app.sqlite3", # we need to extract the path part after "jdbc:sqlite:" # The standard URI.parse doesn't handle JDBC URIs well, so we remove the prefix manually diff --git a/repo-sync.yml b/repo-sync.yml index c4c4a859..a5e462b5 100644 --- a/repo-sync.yml +++ b/repo-sync.yml @@ -6,7 +6,6 @@ github_org: hanami ci: matrix: rack: ["~> 2.0", "~> 3.0"] - jruby: false gemspec: authors: ["Hanakai team"] email: ["info@hanakai.org"] diff --git a/spec/integration/hanami/cli/commands/app/server_spec.rb b/spec/integration/hanami/cli/commands/app/server_spec.rb index d2b3ce1d..03d97717 100644 --- a/spec/integration/hanami/cli/commands/app/server_spec.rb +++ b/spec/integration/hanami/cli/commands/app/server_spec.rb @@ -5,32 +5,30 @@ require "puma" RSpec.describe Hanami::CLI::Commands::App::Server do - subject { described_class.new } - it "starts rack server in the given environment" do host = ENV.fetch("HANAMI_CLI_TEST_HOST", "0.0.0.0") port = ENV.fetch("HANAMI_CLI_TEST_PORT", "2300") + app_root = File.join(__dir__, "../../../../../fixtures/test") begin - pid = fork do - $stdout.reopen "/dev/null", "a" - $stderr.reopen "/dev/null", "a" - subject.call( - config: File.join(File.dirname(__FILE__), "../../../../../fixtures/test/config.ru"), - host: host, - port: port, - env: "staging" - ) - end + # Spawn a new process rather than forking, since fork is not available on JRuby + pid = Process.spawn( + "bin/hanami", "server", "--host=#{host}", "--port=#{port}", "--env=staging", + chdir: app_root, out: File::NULL, err: File::NULL + ) response = open_uri("http://#{host}:#{port}/") expect(response).to eq("Hello, world! (staging)") ensure - Process.kill(:KILL, pid) + if pid + Process.kill(:KILL, pid) + Process.wait(pid) + end end end - def open_uri(uri, attempts = 5) + # Allow time for the new process to boot (including on JRuby) + def open_uri(uri, attempts = 30) URI.open(uri).read # rubocop:disable Security/Open rescue Errno::ECONNREFUSED raise if attempts.zero? diff --git a/spec/support/helpers.rb b/spec/support/helpers.rb index c35d6a2e..4aeb6d23 100644 --- a/spec/support/helpers.rb +++ b/spec/support/helpers.rb @@ -27,6 +27,23 @@ def sqlite_db_name(url, dir: nil) jruby? && dir ? File.join(dir, url) : url end + def postgres_url(db_suffix) + url = "#{POSTGRES_BASE_URL}#{db_suffix}" + jruby? ? jdbc_url(url, scheme: "postgresql") : url + end + + def mysql_url(db_suffix) + url = "#{MYSQL_BASE_URL}#{db_suffix}" + jruby? ? jdbc_url(url, scheme: "mysql") : url + end + + def jdbc_url(url, scheme:) + uri = URI(url) + # JDBC drivers expect the user and password as query params, not in the userinfo + query = URI.encode_www_form({user: uri.user, password: uri.password}.compact) + "jdbc:#{scheme}://#{uri.host}:#{uri.port}#{uri.path}?#{query}" + end + def jruby? RUBY_ENGINE == "jruby" end diff --git a/spec/unit/hanami/cli/commands/app/db/create_spec.rb b/spec/unit/hanami/cli/commands/app/db/create_spec.rb index 25526cb9..6cd465bd 100644 --- a/spec/unit/hanami/cli/commands/app/db/create_spec.rb +++ b/spec/unit/hanami/cli/commands/app/db/create_spec.rb @@ -76,7 +76,7 @@ def before_prepare describe "postgres", :postgres do before do - ENV["DATABASE_URL"] = "#{POSTGRES_BASE_URL}_app" + ENV["DATABASE_URL"] = postgres_url("_app") end it "creates the database" do @@ -101,7 +101,7 @@ def before_prepare describe "mysql", :mysql do before do - ENV["DATABASE_URL"] = "#{MYSQL_BASE_URL}_app" + ENV["DATABASE_URL"] = mysql_url("_app") end it "creates the database" do @@ -253,8 +253,8 @@ def before_prepare describe "postgres", :postgres do before do - ENV["DATABASE_URL"] = "#{POSTGRES_BASE_URL}_app" - ENV["MAIN__DATABASE_URL"] = "#{POSTGRES_BASE_URL}_main" + ENV["DATABASE_URL"] = postgres_url("_app") + ENV["MAIN__DATABASE_URL"] = postgres_url("_main") end it "creates each database" do diff --git a/spec/unit/hanami/cli/commands/app/db/drop_spec.rb b/spec/unit/hanami/cli/commands/app/db/drop_spec.rb index f6458854..c596e1be 100644 --- a/spec/unit/hanami/cli/commands/app/db/drop_spec.rb +++ b/spec/unit/hanami/cli/commands/app/db/drop_spec.rb @@ -306,8 +306,8 @@ def before_prepare describe "postgres", :postgres do before do - ENV["DATABASE_URL"] = "#{POSTGRES_BASE_URL}_app" - ENV["MAIN__DATABASE_URL"] = "#{POSTGRES_BASE_URL}_main" + ENV["DATABASE_URL"] = postgres_url("_app") + ENV["MAIN__DATABASE_URL"] = postgres_url("_main") end it "drops each database" do @@ -427,7 +427,7 @@ def before_prepare describe "mysql", :mysql do before do - ENV["DATABASE_URL"] = "#{MYSQL_BASE_URL}_app" + ENV["DATABASE_URL"] = mysql_url("_app") end it "drops the database" do diff --git a/spec/unit/hanami/cli/commands/app/db/migrate_spec.rb b/spec/unit/hanami/cli/commands/app/db/migrate_spec.rb index f7dd51ba..8026f138 100644 --- a/spec/unit/hanami/cli/commands/app/db/migrate_spec.rb +++ b/spec/unit/hanami/cli/commands/app/db/migrate_spec.rb @@ -246,8 +246,8 @@ def before_prepare describe "postgres", :postgres do before do - ENV["DATABASE_URL"] = "#{POSTGRES_BASE_URL}_app" - ENV["MAIN__DATABASE_URL"] = "#{POSTGRES_BASE_URL}_main" + ENV["DATABASE_URL"] = postgres_url("_app") + ENV["MAIN__DATABASE_URL"] = postgres_url("_main") db_create end diff --git a/spec/unit/hanami/cli/commands/app/db/prepare_spec.rb b/spec/unit/hanami/cli/commands/app/db/prepare_spec.rb index 6e631f14..6fe8db8f 100644 --- a/spec/unit/hanami/cli/commands/app/db/prepare_spec.rb +++ b/spec/unit/hanami/cli/commands/app/db/prepare_spec.rb @@ -191,8 +191,8 @@ class Comments < Hanami::DB::Relation describe "postgres", :postgres do before do - ENV["DATABASE_URL"] = "#{POSTGRES_BASE_URL}_app" - ENV["MAIN__DATABASE_URL"] = "#{POSTGRES_BASE_URL}_main" + ENV["DATABASE_URL"] = postgres_url("_app") + ENV["MAIN__DATABASE_URL"] = postgres_url("_main") end context "from scratch, with structure dump and seeds" do diff --git a/spec/unit/hanami/cli/commands/app/db/rollback_spec.rb b/spec/unit/hanami/cli/commands/app/db/rollback_spec.rb index 9b35908f..47f69ec0 100644 --- a/spec/unit/hanami/cli/commands/app/db/rollback_spec.rb +++ b/spec/unit/hanami/cli/commands/app/db/rollback_spec.rb @@ -351,8 +351,8 @@ class App < Hanami::App describe "postgres", :postgres do before do - ENV["DATABASE_URL"] = "#{POSTGRES_BASE_URL}_app" - ENV["MAIN__DATABASE_URL"] = "#{POSTGRES_BASE_URL}_main" + ENV["DATABASE_URL"] = postgres_url("_app") + ENV["MAIN__DATABASE_URL"] = postgres_url("_main") end context "single database" do @@ -394,9 +394,9 @@ def before_prepare_gateways end before do - ENV["DATABASE_URL"] = "#{POSTGRES_BASE_URL}_app" - ENV["DATABASE_URL__EXTRA"] = "#{POSTGRES_BASE_URL}_extra" - ENV["DATABASE_URL__SUPER"] = "#{POSTGRES_BASE_URL}_super" + ENV["DATABASE_URL"] = postgres_url("_app") + ENV["DATABASE_URL__EXTRA"] = postgres_url("_extra") + ENV["DATABASE_URL__SUPER"] = postgres_url("_super") with_directory(@dir = make_tmp_directory) do write "config/app.rb", <<~RUBY @@ -424,8 +424,8 @@ class App < Hanami::App describe "mysql", :mysql do before do - ENV["DATABASE_URL"] = "#{MYSQL_BASE_URL}_app" - ENV["MAIN__DATABASE_URL"] = "#{MYSQL_BASE_URL}_main" + ENV["DATABASE_URL"] = mysql_url("_app") + ENV["MAIN__DATABASE_URL"] = mysql_url("_main") end context "single database" do @@ -467,9 +467,9 @@ def before_prepare_gateways end before do - ENV["DATABASE_URL"] = "#{MYSQL_BASE_URL}_app" - ENV["DATABASE_URL__EXTRA"] = "#{MYSQL_BASE_URL}_extra" - ENV["DATABASE_URL__SUPER"] = "#{MYSQL_BASE_URL}_super" + ENV["DATABASE_URL"] = mysql_url("_app") + ENV["DATABASE_URL__EXTRA"] = mysql_url("_extra") + ENV["DATABASE_URL__SUPER"] = mysql_url("_super") with_directory(@dir = make_tmp_directory) do write "config/app.rb", <<~RUBY diff --git a/spec/unit/hanami/cli/commands/app/db/structure/dump_spec.rb b/spec/unit/hanami/cli/commands/app/db/structure/dump_spec.rb index d2de9b11..fa84ffaf 100644 --- a/spec/unit/hanami/cli/commands/app/db/structure/dump_spec.rb +++ b/spec/unit/hanami/cli/commands/app/db/structure/dump_spec.rb @@ -217,8 +217,8 @@ def before_prepare describe "postgres", :postgres do before do - ENV["DATABASE_URL"] = "#{POSTGRES_BASE_URL}_app" - ENV["MAIN__DATABASE_URL"] = "#{POSTGRES_BASE_URL}_main" + ENV["DATABASE_URL"] = postgres_url("_app") + ENV["MAIN__DATABASE_URL"] = postgres_url("_main") db_migrate end @@ -299,7 +299,7 @@ def before_prepare describe "mysql", :mysql do before do - ENV["DATABASE_URL"] = "#{MYSQL_BASE_URL}_app" + ENV["DATABASE_URL"] = mysql_url("_app") db_migrate end diff --git a/spec/unit/hanami/cli/commands/app/db/structure/load_spec.rb b/spec/unit/hanami/cli/commands/app/db/structure/load_spec.rb index a27dfdf5..a423e2a0 100644 --- a/spec/unit/hanami/cli/commands/app/db/structure/load_spec.rb +++ b/spec/unit/hanami/cli/commands/app/db/structure/load_spec.rb @@ -190,8 +190,8 @@ def before_prepare describe "postgres", :postgres do before do - ENV["DATABASE_URL"] = "#{POSTGRES_BASE_URL}_app" - ENV["MAIN__DATABASE_URL"] = "#{POSTGRES_BASE_URL}_main" + ENV["DATABASE_URL"] = postgres_url("_app") + ENV["MAIN__DATABASE_URL"] = postgres_url("_main") db_structure_dump end @@ -249,7 +249,7 @@ def before_prepare describe "mysql", :mysql do before do - ENV["DATABASE_URL"] = "#{MYSQL_BASE_URL}_app" + ENV["DATABASE_URL"] = mysql_url("_app") db_structure_dump end