From 8aa0c48834f7f5ca4fe646b174f7eeaaa6a8a4c1 Mon Sep 17 00:00:00 2001 From: Theodor Tonum Date: Sat, 16 Mar 2024 18:37:02 +0100 Subject: [PATCH 1/3] chore: remove pg gem dependency --- Gemfile.lock | 1 - veksel.gemspec | 1 - 2 files changed, 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 2a101f7..856b903 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,7 +3,6 @@ PATH specs: veksel (0.2.2) activerecord - pg rails (>= 7.1.0, < 8) GEM diff --git a/veksel.gemspec b/veksel.gemspec index 292f395..ec3efaf 100644 --- a/veksel.gemspec +++ b/veksel.gemspec @@ -24,5 +24,4 @@ Gem::Specification.new do |spec| spec.add_dependency "rails", ">= 7.1.0", "< 8" spec.add_dependency "activerecord" - spec.add_dependency "pg" end From 4e5aa2f2f62b6e5a09a13e361242d367b06e2325 Mon Sep 17 00:00:00 2001 From: Theodor Tonum Date: Sat, 16 Mar 2024 14:44:19 +0100 Subject: [PATCH 2/3] ref: add Veksel.adapter_for --- lib/tasks/veksel_tasks.rake | 12 ++++++++++-- lib/veksel.rb | 13 +++++++++++++ lib/veksel/cli.rb | 12 +++++++----- lib/veksel/commands/clean.rb | 8 +++----- lib/veksel/commands/fork.rb | 15 ++++++++------- lib/veksel/commands/list.rb | 4 +--- lib/veksel/railtie.rb | 15 +++++++-------- test/dummy/config/database.sqlite.yml | 7 +++++++ test/veksel_test.rb | 20 ++++++++++++++++++++ 9 files changed, 76 insertions(+), 30 deletions(-) create mode 100644 test/dummy/config/database.sqlite.yml diff --git a/lib/tasks/veksel_tasks.rake b/lib/tasks/veksel_tasks.rake index 802febd..93e61af 100644 --- a/lib/tasks/veksel_tasks.rake +++ b/lib/tasks/veksel_tasks.rake @@ -11,7 +11,11 @@ namespace :veksel do require 'veksel/commands/list' db = ActiveRecord::Base.configurations.find_db_config('development') - Veksel::Commands::List.new(db).perform + begin + Veksel::Commands::List.new(db).perform + rescue Veksel::AdapterNotSupported => e + $stderr.puts e.message + end end desc "Delete forked databases" @@ -19,6 +23,10 @@ namespace :veksel do require 'veksel/commands/clean' db = ActiveRecord::Base.configurations.find_db_config('development') - Veksel::Commands::Clean.new(db).perform + begin + Veksel::Commands::Clean.new(db).perform + rescue Veksel::AdapterNotSupported => e + $stderr.puts "#{e.message} - clean skipped" + end end end diff --git a/lib/veksel.rb b/lib/veksel.rb index 734f8bc..8fe5bac 100644 --- a/lib/veksel.rb +++ b/lib/veksel.rb @@ -9,7 +9,20 @@ require "veksel/suffix" module Veksel + class AdapterNotSupported < StandardError; end + class << self + def adapter_for(config, exception: true) + case config[:adapter] + when 'postgresql' + require_relative './veksel/pg_cluster' + Veksel::PgCluster.new(config) + else + return unless exception + raise AdapterNotSupported, "Veksel does not yet support #{config[:adapter]}" + end + end + def current_branch `git rev-parse --abbrev-ref HEAD`.strip end diff --git a/lib/veksel/cli.rb b/lib/veksel/cli.rb index 4aa1e8a..70003b6 100644 --- a/lib/veksel/cli.rb +++ b/lib/veksel/cli.rb @@ -24,11 +24,13 @@ def fork target_database = config[:database] + Veksel.suffix db = OpenStruct.new(configuration_hash: config, database: target_database) - Veksel::Commands::Fork.new(db).perform - - duration = ((Time.now.to_f - t1) * 1000).to_i - FileUtils.touch('tmp/restart.txt') - puts "Forked database in #{duration.to_i}ms" + if Veksel::Commands::Fork.new(db).perform + duration = ((Time.now.to_f - t1) * 1000).to_i + FileUtils.touch('tmp/restart.txt') + puts "Forked database in #{duration.to_i}ms" + end + rescue AdapterNotSupported => e + $stderr.puts "#{e.message} - fork skipped" end def read_config(path) diff --git a/lib/veksel/commands/clean.rb b/lib/veksel/commands/clean.rb index e048c44..cdbe28f 100644 --- a/lib/veksel/commands/clean.rb +++ b/lib/veksel/commands/clean.rb @@ -1,22 +1,20 @@ -require_relative '../pg_cluster' - module Veksel module Commands class Clean def initialize(db, dry_run: false) - @pg_cluster = PgCluster.new(db.configuration_hash) + @adapter = Veksel.adapter_for(db.configuration_hash) @dry_run = dry_run end def perform - all_databases = @pg_cluster.forked_databases + all_databases = @adapter.forked_databases active_branches = Veksel.active_branches stale_databases = all_databases.filter do |database| active_branches.none? { |branch| database.end_with?("_#{branch}") } end stale_databases.each do |database| - @pg_cluster.drop_database(database, dry_run: @dry_run) + @adapter.drop_database(database, dry_run: @dry_run) end end end diff --git a/lib/veksel/commands/fork.rb b/lib/veksel/commands/fork.rb index eb762e2..b30576c 100644 --- a/lib/veksel/commands/fork.rb +++ b/lib/veksel/commands/fork.rb @@ -1,22 +1,23 @@ -require_relative '../pg_cluster' - module Veksel module Commands class Fork - attr_reader :pg_cluster, :source_db, :target_db + attr_reader :adapter, :source_db, :target_db def initialize(db) - @pg_cluster = PgCluster.new(db.configuration_hash) + @adapter = Veksel.adapter_for(db.configuration_hash) @source_db = db.database.sub(%r[#{Veksel.suffix}$], '') @target_db = db.database raise "Source and target databases cannot be the same" if source_db == target_db end def perform - return if pg_cluster.target_populated?(target_db) + return false unless adapter + return false if adapter.target_populated?(target_db) + + adapter.kill_connection(source_db) + adapter.create_database(target_db, template: source_db) - pg_cluster.kill_connection(source_db) - pg_cluster.create_database(target_db, template: source_db) + true end end end diff --git a/lib/veksel/commands/list.rb b/lib/veksel/commands/list.rb index 7fbf6eb..af78a20 100644 --- a/lib/veksel/commands/list.rb +++ b/lib/veksel/commands/list.rb @@ -1,12 +1,10 @@ -require_relative '../pg_cluster' - module Veksel module Commands class List attr_reader :adapter def initialize(db) - @adapter = PgCluster.new(db.configuration_hash) + @adapter = Veksel.adapter_for(db.configuration_hash) end def perform diff --git a/lib/veksel/railtie.rb b/lib/veksel/railtie.rb index d1d6393..72b60be 100644 --- a/lib/veksel/railtie.rb +++ b/lib/veksel/railtie.rb @@ -11,15 +11,14 @@ class Railtie < ::Rails::Railtie ActiveRecord::DatabaseConfigurations.register_db_config_handler do |env_name, name, url, config| next if url.present? next unless env_name == 'development' || env_name == 'test' + veksel_adapter = Veksel.adapter_for(config, exception: false) + database_name = "#{config[:database]}#{Veksel.suffix}" + next unless veksel_adapter&.target_populated?(database_name) - if PgCluster.new(config).target_populated?("#{config[:database]}#{Veksel.suffix}") - ActiveRecord::DatabaseConfigurations::HashConfig.new(env_name, name, config.merge({ - database: "#{config[:database]}#{Veksel.suffix}", - veksel_main_database: config[:database], - })) - else - ActiveRecord::DatabaseConfigurations::HashConfig.new(env_name, name, config) - end + ActiveRecord::DatabaseConfigurations::HashConfig.new(env_name, name, config.merge({ + database: database_name, + veksel_main_database: config[:database], + })) end end end diff --git a/test/dummy/config/database.sqlite.yml b/test/dummy/config/database.sqlite.yml new file mode 100644 index 0000000..8923401 --- /dev/null +++ b/test/dummy/config/database.sqlite.yml @@ -0,0 +1,7 @@ +development: + adapter: sqlite3 + database: db/development.sqlite3 + +test: + adapter: sqlite3 + database: db/test.sqlite3 diff --git a/test/veksel_test.rb b/test/veksel_test.rb index 796f801..d7d455a 100644 --- a/test/veksel_test.rb +++ b/test/veksel_test.rb @@ -65,6 +65,26 @@ def run_fork_test end end + test "veksel fork should warn on unsupported database adapters" do + swap_db_config('database.sqlite.yml') do + Dir.chdir('test/dummy') do + begin + system!('bin/rails db:setup') + system!('bin/rails veksel:clean') + + git_checkout('somebranch') do + Tempfile.open do |buffer| + system!('bundle exec veksel fork', exception: false, err: buffer.path.to_s) + buffer.rewind + output = buffer.read + assert_match /Veksel does not yet support sqlite3 - fork skipped/, output + end + end + end + end + end + end + test "performance" do def measure_in_ms(&blk) t0 = (Time.now.to_f * 1000).to_i From a50c535c329a6b0d61c142b65bec7b076e28ef4e Mon Sep 17 00:00:00 2001 From: Theodor Tonum Date: Sat, 16 Mar 2024 17:04:00 +0100 Subject: [PATCH 3/3] ref!: move db naming conventions to PgCluster BREAKING CHANGE: * Veksel.prefix has been removed * Veksel.suffix no longer has a leading underscore * CLI: suffix command has been removed --- bin/veksel | 2 -- lib/veksel.rb | 4 ---- lib/veksel/cli.rb | 9 +++------ lib/veksel/commands/clean.rb | 4 ++-- lib/veksel/commands/fork.rb | 4 ++-- lib/veksel/commands/list.rb | 4 ++-- lib/veksel/pg_cluster.rb | 15 ++++++++++++--- lib/veksel/railtie.rb | 6 ++++-- lib/veksel/suffix.rb | 2 +- test/pg_cluster_test.rb | 10 ++++++++++ test/veksel_test.rb | 16 ++++++++++++++++ 11 files changed, 52 insertions(+), 24 deletions(-) create mode 100644 test/pg_cluster_test.rb diff --git a/bin/veksel b/bin/veksel index 2176d4e..8c27d1c 100755 --- a/bin/veksel +++ b/bin/veksel @@ -2,8 +2,6 @@ require 'veksel/cli' case ARGV[0] -when 'suffix' - Veksel::CLI.suffix when 'fork' Veksel::CLI.fork end diff --git a/lib/veksel.rb b/lib/veksel.rb index 8fe5bac..80a8277 100644 --- a/lib/veksel.rb +++ b/lib/veksel.rb @@ -38,9 +38,5 @@ def skip_fork? def suffix Suffix.new(current_branch).to_s end - - def prefix(dbname) - dbname.sub(%r[#{Veksel.suffix}$], '_') - end end end diff --git a/lib/veksel/cli.rb b/lib/veksel/cli.rb index 70003b6..1c5e816 100644 --- a/lib/veksel/cli.rb +++ b/lib/veksel/cli.rb @@ -2,18 +2,15 @@ module Veksel module CLI - class << self - def suffix - print Veksel.suffix - end + DbConfig = Struct.new('DbConfig', :configuration_hash) + class << self def fork return if Veksel.skip_fork? t1 = Time.now.to_f require 'veksel/commands/fork' require 'psych' - require 'ostruct' require 'fileutils' config = read_config('config/database.yml')[:development] @@ -23,7 +20,7 @@ def fork end target_database = config[:database] + Veksel.suffix - db = OpenStruct.new(configuration_hash: config, database: target_database) + db = DbConfig.new(config) if Veksel::Commands::Fork.new(db).perform duration = ((Time.now.to_f - t1) * 1000).to_i FileUtils.touch('tmp/restart.txt') diff --git a/lib/veksel/commands/clean.rb b/lib/veksel/commands/clean.rb index cdbe28f..d08df79 100644 --- a/lib/veksel/commands/clean.rb +++ b/lib/veksel/commands/clean.rb @@ -11,10 +11,10 @@ def perform active_branches = Veksel.active_branches stale_databases = all_databases.filter do |database| - active_branches.none? { |branch| database.end_with?("_#{branch}") } + active_branches.none? { |branch| database.branch == branch } end stale_databases.each do |database| - @adapter.drop_database(database, dry_run: @dry_run) + @adapter.drop_database(database.name, dry_run: @dry_run) end end end diff --git a/lib/veksel/commands/fork.rb b/lib/veksel/commands/fork.rb index b30576c..9042774 100644 --- a/lib/veksel/commands/fork.rb +++ b/lib/veksel/commands/fork.rb @@ -5,8 +5,8 @@ class Fork def initialize(db) @adapter = Veksel.adapter_for(db.configuration_hash) - @source_db = db.database.sub(%r[#{Veksel.suffix}$], '') - @target_db = db.database + @source_db = adapter.main_database + @target_db = adapter.db_name_for_suffix(Veksel.suffix) raise "Source and target databases cannot be the same" if source_db == target_db end diff --git a/lib/veksel/commands/list.rb b/lib/veksel/commands/list.rb index af78a20..dab2da6 100644 --- a/lib/veksel/commands/list.rb +++ b/lib/veksel/commands/list.rb @@ -18,8 +18,8 @@ def perform hash = {} databases.each do |database| - branch = database.sub(adapter.forked_database_prefix, '') - hash[branch] = database + branch = database.branch + hash[branch] = database.name end longest_branch_name = hash.keys.max_by(&:length).length diff --git a/lib/veksel/pg_cluster.rb b/lib/veksel/pg_cluster.rb index a08d2db..dc5230d 100644 --- a/lib/veksel/pg_cluster.rb +++ b/lib/veksel/pg_cluster.rb @@ -1,5 +1,7 @@ module Veksel class PgCluster + Database = Struct.new(:name, :branch) + attr_reader :configuration_hash def initialize(configuration_hash) @@ -11,11 +13,14 @@ def main_database end def forked_databases - list_databases(prefix: forked_database_prefix) + list_databases(prefix: forked_database_prefix).map do |name| + Database.new(name, name.sub(forked_database_prefix, '')) + end end - def forked_database_prefix - "#{main_database}_" + def db_name_for_suffix(suffix) + return main_database if suffix.empty? + "#{forked_database_prefix}#{suffix}" end def target_populated?(dbname) @@ -54,6 +59,10 @@ def drop_database(dbname, dry_run: false) private + def forked_database_prefix + "#{main_database}_" + end + def pg_connection_args(dbname) "-d #{dbname} --no-password" end diff --git a/lib/veksel/railtie.rb b/lib/veksel/railtie.rb index 72b60be..bc27495 100644 --- a/lib/veksel/railtie.rb +++ b/lib/veksel/railtie.rb @@ -12,8 +12,10 @@ class Railtie < ::Rails::Railtie next if url.present? next unless env_name == 'development' || env_name == 'test' veksel_adapter = Veksel.adapter_for(config, exception: false) - database_name = "#{config[:database]}#{Veksel.suffix}" - next unless veksel_adapter&.target_populated?(database_name) + next unless veksel_adapter + + database_name = veksel_adapter.db_name_for_suffix(Veksel.suffix) + next unless veksel_adapter.target_populated?(database_name) ActiveRecord::DatabaseConfigurations::HashConfig.new(env_name, name, config.merge({ database: database_name, diff --git a/lib/veksel/suffix.rb b/lib/veksel/suffix.rb index 5a6d9dc..72ca2aa 100644 --- a/lib/veksel/suffix.rb +++ b/lib/veksel/suffix.rb @@ -11,7 +11,7 @@ def to_s when *PROTECTED_BRANCHES "" else - "_#{@branch_name}" + @branch_name end end end diff --git a/test/pg_cluster_test.rb b/test/pg_cluster_test.rb new file mode 100644 index 0000000..897b59d --- /dev/null +++ b/test/pg_cluster_test.rb @@ -0,0 +1,10 @@ +require "test_helper" + + +class VekselPgClusterTest < ActiveSupport::TestCase + test "db_name_for_suffix" do + pg_cluster = Veksel::PgCluster.new({ database: 'veksel_dummy_development' }) + assert_equal 'veksel_dummy_development_somebranch', pg_cluster.db_name_for_suffix('somebranch') + assert_equal 'veksel_dummy_development', pg_cluster.db_name_for_suffix('') + end +end diff --git a/test/veksel_test.rb b/test/veksel_test.rb index d7d455a..0fd6dc5 100644 --- a/test/veksel_test.rb +++ b/test/veksel_test.rb @@ -26,6 +26,22 @@ class VekselTest < ActiveSupport::TestCase assert Veksel::VERSION end + test "use default database in main branch" do + Dir.chdir('test/dummy') do + current_db = `bin/rails runner "print ApplicationRecord.connection.execute('SELECT current_database();')[0]['current_database']"`.chomp + assert_equal 'veksel_dummy_development', current_db + end + end + + test "use default database in master branch" do + Dir.chdir('test/dummy') do + git_checkout('master') do + current_db = `bin/rails runner "print ApplicationRecord.connection.execute('SELECT current_database();')[0]['current_database']"`.chomp + assert_equal 'veksel_dummy_development', current_db + end + end + end + class IntegrationTests < ActiveSupport::TestCase include TestHelpers